0 工作总结
- 当天问题,当天解决,坚决不过周。(2017/5/7)
- Git提交前先review代码,确认无误再提交;每次提交的内容是一个功能点,方便查找功能、代码。(2017/7/2)
- 每一个功能点都必须经过自测,才能提交测试。(2017/7/2)
- 控制情绪。(2017/8/8)
- 考虑问题更加全面,非常注意:新版本、老版本互相兼容(2017/12/7)
- 对需求或者问题,能够自己独立思考(独立分析程序、分析日志),通过高明的方法(类似:单元测试,不需要配一个点一个),得出解决问题的思路和方案。(2018/1/17)
- 使用工程师的思维考虑问题,例如:前瞻性和兼容性。不是凭感觉,而是找出依据,理性分析问题,找出解决方案。(2018/1/17)
- 学习到的知识,除了探究原理,还得思考如何应用到实际工作开发中。(2018/1/17)
- 为什么要这样,不这样会怎么样,有没有更好的方式,尽量在自己的项目中用到自己学的东西(2018/4/13)
- 遇到问题是直面解决,而不是选择容易的,只有解决了才能真正有收获(2018/5/2)
- 知识无穷无尽,关键是把自己做过的研究透(2018/5/4)
- 做完是对别人靠谱,做好是对自己靠谱,只有做到极致才会有开挂的人生。当没做完一件事情,问自己一句:这件事我做完了,我自己真的满意么?准备收工时再多问自己一句:我真的没办法优化一点点了么,那怕一点点?(2018/5/24)
1 assets存储原始文件
assets下文件在打包生成apk的时候不会被编译,以文件原有的方式来保存,可以通过AssetManager来操作这些文件。避免了存储在res文件下的文件模糊、压缩等问题。
系统在编译的时候不会编译assets下的资源文件,所以我们不能通过R.xxx.ID的方式访问它们。那我么能不能通过该资源的绝对路径去访问它们呢?因为apk安装之后会放在/data/app/**.apk目录下,以apk形式存在,asset/res和被绑定在apk里,并不会解压到/data/data/YourApp目录下去,所以我们无法直接获取到assets的绝对路径,因为它们根本就没有。
1.1 加载assets目录下图片或网页
ImageLoader.getInstance().displayImage(Uri.parse("file:///android_asset/ic_qrcode_default.png"), ivQrCode, ImageLoaderOptions.getOptionQrCode());
1.2 AssetManager类
(1)访问assets目录下的资源文件
AssetManager.open(String filename),返回的是一个InputSteam类型的字节流,这里的filename必须是文件比如(aa.txt;img/semll.jpg),而不能是文件夹。
AssetManager assetManager = getResources().getAssets();
InputStream inputStream = assetManager.open("fungo_cst_src.txt");
(2)获取assets的文件及目录名
// 获取assets目录下的所有文件及目录名,content
//(当前的上下文如Activity,Service等ContextWrapper的子类的都可以)
String fileNames[] =context.getAssets().list(path);
1.3 asset目录与res目录的区别
(1)res 目录下面有很多文件,例如 drawable,mipmap,raw 等。res 下面除了 raw 文件不会被压缩外,其余文件都会被压缩。同时 res目录下的文件可以通过R 文件访问。
(2)asset 也是用来存储资源,但是 asset 文件内容只能通过路径或者 AssetManager 读取。
2 字符串格式化-String.format()的使用
2.1 format()方法
(1)String类的format()方法用于创建格式化的字符串以及连接多个字符串对象。熟悉C语言的同学应该记得c语言的sprintf()方法,两者有类似之处。format()方法有两种重载形式。
(2)format(String format, Object… args) 新字符串使用本地语言环境,制定字符串格式和参数生成格式化的新字符串。
(3)format(Locale locale, String format, Object… args) 使用指定的语言环境,制定字符串格式和参数生成格式化的字符串。
2.2 常规类型的格式化
(1)常规类型
(2)测试用例
public static void main(String[] args) {
String str=null;
str=String.format("Hi,%s", "王力");
System.out.println(str);
str=String.format("Hi,%s:%s.%s", "王南","王力","王张");
System.out.println(str);
System.out.printf("字母a的大写是:%c %n", 'A');
System.out.printf("3>7的结果是:%b %n", 3>7);
System.out.printf("100的一半是:%d %n", 100/2);
System.out.printf("100的16进制数是:%x %n", 100);
System.out.printf("100的8进制数是:%o %n", 100);
System.out.printf("50元的书打8.5折扣是:%f 元%n", 50*0.85);
System.out.printf("上面价格的16进制数是:%a %n", 50*0.85);
System.out.printf("上面价格的指数表示:%e %n", 50*0.85);
System.out.printf("上面价格的指数和浮点数结果的长度较短的是:%g %n", 50*0.85);
System.out.printf("上面的折扣是%d%% %n", 85);
System.out.printf("字母A的散列码是:%h %n", 'A');
(3)结果
Hi,王力
Hi,王南:王力.王张
字母a的大写是:A
3>7的结果是:false
100的一半是:50
100的16进制数是:64
100的8进制数是:144
50元的书打8.5折扣是:42.500000 元
上面价格的16进制数是:0x1.54p5
上面价格的指数表示:4.250000e+01
上面价格的指数和浮点数结果的长度较短的是:42.5000
上面的折扣是85%
字母A的散列码是:41
2.3 其他参考链接
3 彻底解决INSTALL_FAILED_UPDATE_INCOMPATIBLE的安装错误
彻底解决INSTALL_FAILED_UPDATE_INCOMPATIBLE的安装错误
4 监听home键的实现方式
以前写的那些方式都不是很好用。现在的这种方式通过广播的方式监听home键:
(1)首先是创建一个广播接受者;
(2)注册监听;
(3)完整代码:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//注册广播
registerReceiver(mHomeKeyEventReceiver, new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
}
/**
* 监听是否点击了home键将客户端推到后台
*/
private BroadcastReceiver mHomeKeyEventReceiver = new BroadcastReceiver() {
String SYSTEM_REASON = "reason";
String SYSTEM_HOME_KEY = "homekey";
String SYSTEM_HOME_KEY_LONG = "recentapps";
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {
String reason = intent.getStringExtra(SYSTEM_REASON);
if (TextUtils.equals(reason, SYSTEM_HOME_KEY)) {
//表示按了home键,程序到了后台
} else if(TextUtils.equals(reason, SYSTEM_HOME_KEY_LONG)){
//表示长按home键,显示最近使用的程序列表
}
}
}
};
}
5 返回键的监听及处理
/**
* Demo描述: 处理Back键按下事件
* 注意事项: 以下两种方法勿一起使用
*/
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
/**
* 方法1: 监听Back键按下事件
* 注意: super.onBackPressed()会自动调用finish()方法,关闭当前Activity. 若要屏蔽Back键盘,注释该行代码即可
*/
@Override
public void onBackPressed() {
super.onBackPressed();
System.out.println("按下了back键 onBackPressed()");
}
/**
* 方法2: 监听Back键按下事件
* 注意: 返回值表示:是否能完全处理该事件在此处返回false,所以会继续传播该事件.在具体项目中此处的返回值视情况而定.
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK)) {
System.out.println("按下了back键 onKeyDown()");
return false;
}else {
return super.onKeyDown(keyCode, event);
}
}
}
6 android:configChanges属性和监听横竖屏切换
6.1 对Android:configChanges属性的认识:
(1)不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次。
(2)设置Activity的android:configChanges=”orientation”时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次。
(3)设置Activity的android:configChanges=”orientation|keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法。
(4)但是,自从Android 3.2(API 13),在设置Activity的android:configChanges=”orientation|keyboardHidden”后,还是会重新调用各个生命周期的。因为screen size也开始跟着设备的横竖切换而改变。所以,在AndroidManifest.xml里设置的MiniSdkVersion和 TargetSdkVersion属性大于等于13的情况下,如果你想阻止程序在运行时重新加载Activity,除了设置”orientation”,还必须设置”ScreenSize”。
(5)解决方法:AndroidManifest.xml中设置
android:configChanges="orientation|keyboardHidden|screenSize“
6.2 监听横竖屏切换监听方法
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == this.getResources().getConfiguration().ORIENTATION_PORTRAIT) {// 切换为竖屏
} else if (newConfig.orientation == this.getResources().getConfiguration().ORIENTATION_LANDSCAPE) {// 切换为横屏
}
}
7 安卓中加载布局文件的三种方法
(1)方法1
LinearLayout layout=(LinearLayout)LayoutInflater.from(getApplicationContext()).inflate(R.layout.list_view_item_text, null);
LinearLayout layout=(LinearLayout)LayoutInflater.from(MainActivity.this).inflate(R.layout.list_view_item_text, null);
(2)方法2
LayoutInflater layoutInflater=(LayoutInflater) getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout layout=(LinearLayout)layoutInflater.inflate(R.layout.list_view_item_text, null);
(3)方法3
LinearLayout layout=(LinearLayout)getLayoutInflater().inflate(R.layout.list_view_item_text, null);
LinearLayout layout=(LinearLayout)MainActivity.this.getLayoutInflater().inflate(R.layout.list_view_item_text, null);
8 android.view.WindowManager$BadTokenException崩溃的4种情形
android.view.WindowManager$BadTokenException崩溃的4种情形
9 singleTop或者singleTask的onNewIntent调用时机
启动模式为singleTop时,系统会先检查栈顶是不是该Activity,如果不是的话,它会创建一个该Activity的实例,并启动onCreate函数。如果栈顶是,则不会再创建该Activity,不会执行onCreate函数,而是执行onNewIntent函数来重新启动。同理,当启动模式为singleTask时,若栈顶不是该Activity系统会在栈中寻找是否存在这个实例,如是的话就会把这个实例放在栈顶,并把它之前的实例清除掉,就会执行onNewIntent函数(道理和singleTop一样)。两种情况执行的顺序变为:onPause()–>onStop()—-发送Intent—>onNewIntent()–>onRestart()–>onStart()–>onResume()。如代码所示:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
getIntent();// 获取的是第一次启动Activity传递的值
}
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
intent.getExtra().get---//获取新传递的值
}
10 正则表达式匹配URL
(1) 判断是否是完整的域名
public static boolean isCompleteUrl(String text) {
Pattern p = Pattern.compile("((http|ftp|https)://)(([a-zA-Z0-9\\._-]+\\.[a-zA-Z]{2,6})|([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}))(:[0-9]{1,4})*(/[a-zA-Z0-9\\&%_\\./-~-]*)?", Pattern.CASE_INSENSITIVE);
Matcher matcher = p.matcher(text);
return matcher.find();
}
(2)判断是否是缺少前缀的域名
/**
* 是否是缺少前缀的域名
*/
public static boolean isHalfCompleteUrl(String text) {
Pattern p = Pattern.compile("(([a-zA-Z0-9\\._-]+\\.[a-zA-Z]{2,6})|([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}))(:[0-9]{1,4})*(/[a-zA-Z0-9\\&%_\\./-~-]*)?", Pattern.CASE_INSENSITIVE);
Matcher matcher = p.matcher(text);
return matcher.find();
}
(3)参考链接
Android利用正则表达式如何匹配URL
11 for循环break和continue的区别,附return
(1)break:break用于完全结束一个循环,跳出循环体执行循环后面的语句。
(2)continue:continue是跳过当次循环中剩下的语句,执行下一次循环。
(3)return:结束本方法的执行,返回。
(4)参考链接:
循环结构中break、continue、return和exit的区别
for循环的简介及break和continue的区别
12 在其他线程中更新UI线程的解决方法
(1)Activity.runOnUiThread(Runnable )
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "hah", Toast.LENGTH_SHORT).show();
}
});
(2)子线程调用Handler的sendMessage(message)发送事件
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
//操作界面
myText.setText( 来自网络的信息);
super.handleMessage(msg);
}
};
public class MyThread extends Thread {
public void run() {
// 耗时操作
loadNetWork();
Message msg = new Message();
mHandler.sendMessage(msg);//向Handler发送消息,
}
}
13 时间格式化
(1)日期格式化
/**
* 日期格式化
* @param date
* @return
*/
public static String formatDate(Long date) {
SimpleDateFormat format = new SimpleDateFormat("MM月dd日", Locale.getDefault());// yyyy-MM-dd HH:mm:ss
return format.format(new Date(date));
}
(2)获取系统时间
SimpleDateFormat formatter = new SimpleDateFormat ("yyyy年MM月dd日 HH:mm:ss ");
Date curDate = new Date(System.currentTimeMillis());//获取当前时间
String str = formatter.format(curDate);
(3)比较两个日期是否在同一天
/**
* 比较两个日期是否在同一天
* @param date1
* @param date2
* @return
*/
public static boolean isSameDate(Long date1, Long date2) {
Calendar cal1 = Calendar.getInstance();
cal1.setTimeInMillis(date1);
Calendar cal2 = Calendar.getInstance();
cal2.setTimeInMillis(date2);
boolean isSameYear = cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR);
boolean isSameMonth = isSameYear && cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH);
boolean isSameDate = isSameMonth && cal1.get(Calendar.DAY_OF_MONTH) == cal2.get(Calendar.DAY_OF_MONTH);
return isSameDate;
}
(4)判断两天在同一周内
/**
* 判断date和当前日期是否在同一周内(开始是周日,结束是周六 )
* 注: 参考(2)获取系统时间先获取系统时间,并转化为Date
*
* Calendar类提供了一个获取日期在所属年份中是第几周的方法,对于上一年末的某一天
* 和新年初的某一天在同一周内也一样可以处理,例如2012-12-31和2013-01-01虽然在
* 不同的年份中,但是使用此方法依然判断二者属于同一周内
*/
public static boolean isSameWeekWithToday(Date date) {
if (date == null) {
return false;
}
// 0.先把Date类型的对象转换Calendar类型的对象
Calendar todayCal = Calendar.getInstance();
Calendar dateCal = Calendar.getInstance();
todayCal.setTime(new Date());
dateCal.setTime(date);
// 1.比较当前日期在年份中的周数是否相同
if (todayCal.get(Calendar.WEEK_OF_YEAR) == dateCal.get(Calendar.WEEK_OF_YEAR)) {
return true;
} else {
return false;
}
}
14 隐式启动Activity
14.1 通过URI或者配置启动Activity
(1)跳转的方法
public static void actionTo(Context context, String name, String url) {
Intent intent = new Intent("android.intent.action.VIEW");
intent.setData(Uri.parse("svmplayer://player?name=" + name + "&url=" + url));
intent.addCategory("android.intent.category.DEFAULT");
intent.addCategory("android.intent.category.BROWSABLE");
context.startActivity(intent);
}
(2)接收处理方法
try {
if (intent != null) {
videoUrl = URLDecoder.decode(intent.getData().getQueryParameter("url"), "UTF-8");
title = URLDecoder.decode(intent.getData().getQueryParameter("name"), "UTF-8");
}
} catch (Exception e) {
Logger.e(e);
}
(3)注册配置
<activity
android:name=".activity.WebOriginActivity"
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:theme="@style/AppTheme"
android:windowSoftInputMode="adjustPan" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="svmplayer" />
</intent-filter>
</activity>
14.2 使用Scheme实现从网页启动APP
15 Java与JS互相调用
15.1 Java调JS(客户端传数据到H5)
(1)首先是JS的一段代码
function javaCallJs(key, value){
document.getElementById("value").innerHTML = ("欢迎:" + value);
}
(2)然后是在java中调用JS中的方法
public abstract class InvokeJsBase {
private static final String rs = "javascript:javaCallJs(\"%s\", %s)";
public String invokeJs() {
String js = String.format(rs, key(), value());
return js;
}
abstract String key();
abstract String value();
}
import com.alibaba.fastjson.JSON;
/**
* 具体实现调用的方法
*/
public class InvokeCollectStatus extends InvokeJsBase {
public boolean value;
public InvokeCollectStatus(boolean value) {
this.value = value;
}
@Override
String key() {
return "statusKey";
}
@Override
String value() {
return JSON.toJSONString(this);
}
}
(3)以上代码就是调用了JS中一个叫javaCallJs(arg)的方法,并传入了一个key、value参数。
15.2 JS调Java(H5传数据到客户端)
(1)配置Javascript接口
webView.addJavascriptInterface(new JSInterface (),"playerClient");
(2)实现Javascript接口类
public class ProgressWebView extends WebView {
private Context context;
public ProgressWebView(final Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
addJavascriptInterface(new JavaScriptinterface(), "playerClient");
}
class JavaScriptinterface {
@JavascriptInterface
public void showToast(String arg) {
Toast.makeText(MainActivity.this, arg, Toast.LENGTH_SHORT).show();
}
}
}
(3)JS中调用java代码
<input type="button" value="点击playerClient被调用" onclick="window.playerClient.showToast('JS中传来的参数')"/>
(4)window.playerClient.showToast(‘JS中传来的参数’)”中的”playerClient”在addJavascriptInterface()中指定的,并且JS向java传递了参数,类型为String。而showToast(String arg)会以Toast的形式弹出此参数。
15.3 WebView详解与简单实现Android与H5互调
16 Java集合互相转换
Java 集合转换(数组、List、Set、Map相互转换)
17 JAVA环境变量的配置(win8)
18 获取本地视频文件以及缩略图
(1)工具类
参考超级影音或者云图手机电视的本地视频例子。
(2)参考例子
19 自定义SeekBar主题
20 Android自定义星星评分控件
21 APP检测安装打开APK
(1)检查APP是否已经安装
public static boolean isPkgInstalled(Context context, String pkgName) {
PackageManager packageManager = context.getPackageManager();
// 获取手机系统的所有APP包名,然后进行一一比较
List<PackageInfo> pinfo = packageManager.getInstalledPackages(0);
for (int i = 0; i < pinfo.size(); i++) {
if (pinfo.get(i).packageName.equalsIgnoreCase(pkgName))
return true;
}
return false;
}
(2)安装已经下载好的apk
private void install(String apkname) {
File dir = mActivity.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
if (dir != null) {
String path = dir.getPath() + "/xiaoai.apk";
File file = new File(path);
if(file.exists()) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(path)),"application/vnd.android.package-archive");
mActivity.startActivity(intent);
} else {
downApk();//安装包已经删除请重新下载
}
} else {
downApk();
}
}
(3)安装已经下载好的apk
private void openApk(String url) {
PackageManager packageManager = mActivity.getPackageManager();
Intent intent = packageManager.getLaunchIntentForPackage("com.fungo.loveshow");
startActivity(intent);
}
(4)打开另一个APP指定的Activity
/**
* 注意:
* 1.需要将目标Activity的android:exported="true"属性在所属应用AndroidMainfest里设置为true,
* 意思是当前Activity可以被外部应用访问。
* 2.需要在当前应用的AndroidMainfest里也声明目标Activity。
*/
public void exportActivity() {
Intent intent = new Intent();
//第一种方式
ComponentName cn = new ComponentName("com.example.fm", "com.example.fm.MainFragmentActivity");
intent.setComponent(cn);
//第二种方式
//intent.setClassName("com.example.fm", "com.example.fm.MainFragmentActivity");
intent.putExtra("test", "intent1");
startActivity(intent);
}
Android APP检测安装打开APK三步操作
Android通过App启动另一个APP
22 实现DownloadManager下载apk,并自动提示安装
实现 DownloadManager 下载完 apk 自动提示安装的功能
23 低版本SDK实现高版本api
(1)一般很多高版本的新的API都会在兼容包里面找到替代的实现。比如:Notification,在v4兼容包里面有NotificationCompat类。5.0+出现的backgroundTint,minSdk小于5.0的话会包检测错误,v4兼容包DrawableCompat类。
(2)在高版本 SDK 中使用高版本 API,低版本 SDK 中自己实现。
// 在使用了高版本API的方法前面加一个@TargetApi(API版本号)。
// 在代码中判断版本号来控制不同的版本使用不同的代码。
@TargetApi(11)
public void text() {
if(Build.VERSION.SDK_INT >= 11){
// 使用 API 11 的方法
} else {
// 使用自己实现的方法
}
24 FastJSON实现Map/Json/String互转
(1)用途-举例:将List转为json存储在本地,再取出转化为List使用
(2)主要方法
// 把JSON文本parse为JSONObject或者JSONArray
public static final Object parse(String text);
// 把JSON文本parse成JSONObject
public static final JSONObject parseObject(String text);
// 把JSON文本parse为JavaBean
public static final <T> T parseObject(String text, Class<T> clazz);
// 把JSON文本parse成JSONArray
public static final JSONArray parseArray(String text);
//把JSON文本parse成JavaBean集合
public static final <T> List<T> parseArray(String text, Class<T> clazz);
// 将JavaBean序列化为JSON文本
public static final String toJSONString(Object object);
// 将JavaBean序列化为带格式的JSON文本
public static final String toJSONString(Object object, boolean prettyFormat);
// 将JavaBean转换为JSONObject或JSONArray。
public static final Object toJSON(Object javaObject);
(3)参考链接
FastJSON 简介及其Map/JSON/String 互转
25 整除(/)、求余(%)、四舍五入
(1)整除(/)、求余(%)
//求余
System.out.println(11%2); //结果--> 1
System.out.println(11%-2); //结果--> 1
System.out.println(-11%2); //结果-->-1
System.out.println(-11%-2); //结果-->-1
//求余的正负号说明: 主要是取决于前面一个数是正数还是负数,不管后面数。
System.out.println(115%5); //结果为-->0
System.out.println(115%2); //结果为-->1
//整除(商)
System.out.println(11/2); //结果--> 5
System.out.println(11/-2); //结果-->-5
System.out.println(-11/2); //结果-->-5
System.out.println(-11/-2); //结果--> 5
System.out.println(10/2); //结果--> 5
System.out.println(10.0/2); //结果--> 5.0
System.out.println(35/2); //结果--> 17
System.out.println(0.35/2); //结果--> 0.175
(2)四舍五入
//方式1
DecimalFormat df = new java.text.DecimalFormat("#.00");
df.format(你要格式化的数字);
//例:#.00 表示两位小数 #.0000四位小数 以此类推...
new java.text.DecimalFormat("#.00").format(3.1415926)
//方式2:%.2f %.表示小数点前任意位数、2表示两位小数、格式后的结果为f表示浮点型
double d = 3.1415926;
String result = String .format("%.2f");
(3)综合示例
@NonNull
public static String getPlayTimes(int data) {
String times;
int MILLION = 10000;
int BILLION = 100000000;
DecimalFormat df = new DecimalFormat("#.0");
if (data < MILLION) {
times = data + "";
} else if (data >= MILLION && data < BILLION){
times = df.format(Double.valueOf(data) / MILLION) + "万";
} else {
times = df.format(Double.valueOf(data) / BILLION) + "亿";
}
Logger.e("正在观看:" + data + " " + data + " " + times);
// 正在观看:48980 48980.0 4.9万
return times;
}
(4)参考链接
Android double保留两位小数:截取 和 四舍五入(展示流量)
26 如何选择compileSdkVersion, minSdkVersion和targetSdkVersion
(1)compileSdkVersion:告诉Gradle用哪个Android SDK版本编译你的应用,需要强调的是修改 compileSdkVersion 不会改变运行时的行为。当你修改了compileSdkVersion 的时候,可能会出现新的编译警告、编译错误,但新的compileSdkVersion不会被包含到APK 中:它纯粹只是在编译的时候使用。强烈推荐总是使用最新的 SDK 进行编译。
(2)minSdkVersion:则是应用可以运行的最低要求。
(3)targetSdkVersion:是Android提供向前兼容的主要依据,在应用的targetSdkVersion没有更新之前系统不会应用最新的行为变化。例如targetSdkVersion对应5.0版本,不会出现6.0的特性。
(4)例子(车载电视-不适用动态权限,把targetSdkVersion调至22)
ext {
compileSdkVersion = 25
buildToolsVersion = "25.0.0"
minSdkVersion = 14
targetSdkVersion = 22
}
(5)学习链接
manifest—–uses-sdk:各Android平台版本支持的API级别,包括各版本亮点
27 判断是否包含SIM卡
/**
* 判断是否包含SIM卡
*/
public static boolean hasSimCard() {
TelephonyManager telMgr = (TelephonyManager)
FungoApplication.getAppContext().getSystemService(Context.TELEPHONY_SERVICE);
boolean result = true;
switch (telMgr.getSimState()) {
case TelephonyManager.SIM_STATE_ABSENT:
// 没有SIM卡
result = false;
break;
case TelephonyManager.SIM_STATE_UNKNOWN:
String subscriberId = telMgr.getSubscriberId();
result = !TextUtils.isEmpty(subscriberId);
break;
default:
break;
}
return result;
}
28 Fragment特点和坑
(1)Fragment嵌套的坑
安卓开发之详解getChildFragmentManager和getFragmentManager详解
(2)Fragment的特点
Fragment的设计主要是把Activity界面包括其逻辑打碎成很多个独立的模块,这样便于模块的重用和更灵活地组装呈现多样的界面。
- Fragment可以作为Activity界面的一个部分组成;
- 可以在一个Activity里面出现多个Fragment,并且一个fragment可以在多个Activity中使用;
- 在Activity运行中,可以动态地添加、删除、替换Fragment。
- Fragment有自己的生命周期的,可以响应输入事件。
(3)Fragment使用的坑和正确的使用姿势
Fragment全解析系列(二):正确的使用姿势
29 Android 截屏检测
参考云图项目的截图检测+上报逻辑。
RxScreenshotDetector:Android 截屏检测
30 广告栏轮播图
31 常用用法
31.1 adb用法
(1)配置adb在高级设置中,在cmd窗口中和AS工具中Terminal直接使用adb命令
(2)学习链接: 一份超全超详细的 ADB 用法大全
31.2 Fiddler对Android应用进行抓包
csdn博客-如何使用Fiddler对Android应用进行抓包
31.3 AS常用快捷键
31.4 App常用图标尺寸规范汇总
31.5 手机开发者常用工具
(1)包括:USB调试;不锁定屏幕、不使用锁屏;显示布局边界;调试GPU过度绘制;GPU呈现模式分析;不保留活动;启用严格模式,应用在主线程执行长时间操作闪烁屏幕;
(2)学习链接:Android调试系列之开发者选项常用功能
31.6 Android颜色值透明度百分比和十六进制对应关系
32 解决包重复
33 RecyclerView添加下拉刷新和上拉加载更多
RecyclerView系列之(3):添加下拉刷新和上拉加载更多
34 异常
(1)Java编程的逻辑—125页;(后续总结文章)
(2) Java异常类层次结构图:
(3)判断一个方法可能会出现异常的依据如下:
①方法中有throw语句;
②调用了其他方法,其他方法的括号后面用throws子句声明抛出某种异常。
(4)学习链接:JAVA 异常分类与理解
35 recycleview和listview获取某个item容易出现空指针问题
public File getItem(int position) {
if (mFileSortBeans != null && mFileSortBeans.size() > position && position >= 0) {
FileSortBean fileSortBean = mFileSortBeans.get(position);
if (fileSortBean != null) {
return fileSortBean.getFile();
}
}
return null;
}
36 布局中的空格以及占一个汉字宽度的空格的实现
(1)效果图
(2)代码实现
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1、姓名" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="2、姓   名" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="3、姓  ‒名" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="4、姓 名" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="5、姓 名" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="6、姓 名" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="7、姓 名" />
<TextView
android:id="@+id/text4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="8、姓名" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="9、姓啥名" />
</LinearLayout>
public class Main2Activity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
TextView text4 = (TextView) findViewById(R.id.text4);
text4.setText("8、姓" + "\u3000" + "名");
}
}
(3)总结:
①7和8都是一个汉字的间隔。其中,在代码中设置text4.setText(“8、姓” + “\u3000” + “名”);的”\u3000”是一个汉字的间隔;
②4是稍大的空格;
③5是稍小的空格。
(4)参考链接
Android布局中的空格以及占一个汉字宽度的空格的实现
37 打印当前的线程及进程
(1)基本方法
// 获取消耗的时间。
android.os.Process.getElapsedCpuTime()
// 获取该进程的ID
android.os.Process.myPid()
// 获取该线程的ID
android.os.Process.myTid()
// 获取该进程的用户ID
android.os.Process.myUid()
// 判断该进程是否支持多进程
android.os.Process.supportsProcesses
(2)参考链接
android如何打印当前的线程及进程