Android进阶之开发问题笔记

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   
10016进制数是:64   
1008进制数是:144   
50元的书打8.5折扣是:42.500000 元  
上面价格的16进制数是:0x1.54p5   
上面价格的指数表示:4.250000e+01   
上面价格的指数和浮点数结果的长度较短的是:42.5000   
上面的折扣是85%   
字母A的散列码是:41   

2.3 其他参考链接

JAVA字符串格式化-String.format()的使用

3 彻底解决INSTALL_FAILED_UPDATE_INCOMPATIBLE的安装错误

彻底解决INSTALL_FAILED_UPDATE_INCOMPATIBLE的安装错误

4 监听home键的实现方式

以前写的那些方式都不是很好用。现在的这种方式通过广播的方式监听home键:
(1)首先是创建一个广播接受者;
(2)注册监听;
(3)完整代码:

扫描二维码关注公众号,回复: 1979213 查看本文章
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

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));
    }

android 时间获取以及时间格式化

(2)获取系统时间

SimpleDateFormat formatter = new SimpleDateFormat ("yyyy年MM月dd日 HH:mm:ss ");  
Date curDate = new Date(System.currentTimeMillis());//获取当前时间  
String str = formatter.format(curDate);  

Android–获取当前系统时间

(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

Android进阶之使用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互调

WebView详解与简单实现Android与H5互调

Android和H5之间的交互

Hybrid混合开发

16 Java集合互相转换

Java 集合转换(数组、List、Set、Map相互转换)

Java数组转成list,list转数组

17 JAVA环境变量的配置(win8)

JAVA环境变量的配置(win8)

18 获取本地视频文件以及缩略图

(1)工具类
  参考超级影音或者云图手机电视的本地视频例子。
  
(2)参考例子

RxJava获取本地视频

android 获取本地视频文件以及缩略图

19 自定义SeekBar主题

自定义SeekBar主题

一个功能强大的自定义SeekBar

20 Android自定义星星评分控件

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;
}

如何通过代码判断手机中是否有SIM卡及各种状态

Android判断手机里是否有SIM卡

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 广告栏轮播图

公司多个项目使用:ConvenientBanner

Android图片轮播控件

31 常用用法

31.1 adb用法

(1)配置adb在高级设置中,在cmd窗口中和AS工具中Terminal直接使用adb命令
(2)学习链接: 一份超全超详细的 ADB 用法大全

31.2 Fiddler对Android应用进行抓包

百度经验-如何用Fiddler对Android应用进行抓包

csdn博客-如何使用Fiddler对Android应用进行抓包

31.3 AS常用快捷键

Android Studio 常用快捷键

31.4 App常用图标尺寸规范汇总

Android必知必会-App 常用图标尺寸规范汇总

31.5 手机开发者常用工具

(1)包括:USB调试;不锁定屏幕、不使用锁屏显示布局边界调试GPU过度绘制GPU呈现模式分析不保留活动;启用严格模式,应用在主线程执行长时间操作闪烁屏幕;
(2)学习链接:Android调试系列之开发者选项常用功能

31.6 Android颜色值透明度百分比和十六进制对应关系

透明度百分比和十六进制对应关系表格

32 解决包重复

完美解决系列—解决包重复

33 RecyclerView添加下拉刷新和上拉加载更多

RecyclerView系列之(3):添加下拉刷新和上拉加载更多

一种优雅的方式实现RecyclerView条目多类型

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、姓&#160;&#160;&#8201;名" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="3、姓&#160;&#160;&#8210;名" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="4、姓&#160;名" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="5、姓&#8201;名" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="6、姓&#8194;名" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="7、姓&#8195;名" />
    <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如何打印当前的线程及进程

猜你喜欢

转载自blog.csdn.net/chenliguan/article/details/73750138