Android学习记录

1.应用性能分析及优化
Android Profiler 工具的使用

2.内存泄漏
内存泄露的原因:
长生命周期的对象持有短生命周期的对象,短周期对象就无法及时释放。
常见内存泄漏:
Android - 常见内存泄露问题盘点
内存泄漏检测工具:
推荐使用leakcanary

3.ANR是什么,如何避免
就是在规定的时间内,没有响应。
三种类型:
1)KeyDispatchTimeout(5 seconds) —主要类型按键或触摸事件在特定时间内无响应
2) BroadcastTimeout(10 seconds) —BroadcastReceiver在特定时间内无法处理完成
3) ServiceTimeout(20 seconds) —小概率类型 Service在特定的时间内无法处理完成
避免方法:
避免在UI线程,BroadcastReceiver 和service主线程中,处理复杂的逻辑和计算

4.创建Thread子线程的方式
1)新建一个类继承自Thread,并重写run()方法

    public static class TestThread extends Thread {
        @Override
        public void run() {
            // 需要执行的代码
            System.out.println("JM startup");
        }
    }

    public static void main(String[] args) {
        TestThread testThread = new TestThread();
        testThread.start();
    }

其中值得注意的是thread.start()方法和thread.run()方法的区别:
① thread.start()是真正创建了一个线程,线程是独立的执行的;在run方法执行完后就进入销毁阶段;
②run方法是一个类中的普通方法,如果直接调用thread.run(),不会创建新线程,仅是在主线程中执行了run方法的调用。

2)实现Runnable接口

    public static class TestThread implements Runnable {
        @Override
        public void run() {
            // 需要执行的代码
            System.out.println("JM startup");
        }
    }

    public static void main(String[] args) {
        Thread testThread = new Thread(new TestThread());
        testThread.start();
    }

3)new一个Thread匿名内部类

    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                // 需要执行的代码
                System.out.println("JM startup");
            }
        });
        thread.start();
    }

5.在子线程上更新ui的方式
1)runOnUiThread;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        startTest();
    }

    private void startTest() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        // 执行更新ui的操作
                    }
                });
            }
        }).start();
    }

需要注意的是,runOnUiThread属于Activity的方法,仅能在Activity中使用,调用runOnUiThread可以在子线程里直接用来更新UI。除此之外,还需在特定情况下避免在runOnUiThread使用耗时操作,以下是runOnUiThread方法源码

    public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }

可以很容易看出来,mUiThread是指主线程,如果当前线程不是主线程,则执行post方法,这是没问题的;但假如是主线程,则会在直接调用run方法,如果在run方法中执行耗时操作,主界面就会卡顿,这是不友好的。

2)handler.post;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        startTest();
    }

    private void startTest() {
        Handler handler = new Handler();
        new Thread(new Runnable() {
            @Override
            public void run() {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        // 执行更新ui的操作
                    }
                });
            }
        }).start();
    }

这里就很熟悉了,这个方法实际上和runOnUiThread源码中的post方法如出一辙,最终都指向了Handler的sendMessageDelayed方法。但是有点需要注意的是,在子线程中不能new Handler,这会导致抛出异常,因为Handler与Looper紧密相关。默认情况下,Android主线程(UI线程)已经有了一个Looper,但对于子线程,你需要手动调用Looper.prepare()来准备Looper环境,并在完成操作后调用Looper.loop()开始循环,在子线程执行这些操作之前,是不能创建Handler的。

3)view.post;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        startTest();
    }

    private void startTest() {
        mButtonStart = (Button) findViewById(R.id.btn_start);
        new Thread(new Runnable() {
            @Override
            public void run() {
                mButtonStart.post(new Runnable() {
                    @Override
                    public void run() {
                        // 执行更新ui的操作
                    }
                });
            }
        }).start();
    }

View.post()方法应该在视图的布局完成后调用,否则可能无法如预期那样工作。

4)handler+message

    private final Handler uiHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what) {
                case 0:
                    Log.e(TAG, "获取what:" + msg.what);
                    // 执行更新ui的操作
                    mButtonStart.setText("测试完成");
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        startTest();
    }

    private void startTest() {
        mButtonStart = (Button) findViewById(R.id.btn_start);
        new Thread(new Runnable() {
            @Override
            public void run() {
                uiHandler.sendEmptyMessage(0);
            }
        }).start();
    }

在使用Handler过程中,极易导致内存泄漏,所以在确保不用的时候,需及时注销。

    @Override
    protected void onDestroy() {
        super.onDestroy();

        uiHandler.removeCallbacksAndMessages(null);
    }

6.开启延时任务的方法
1)handler.postDelayed;

        // 延时1s执行任务
        Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                // 需要执行的延时任务
            }
        }, 1000);

2)view.postDelayed;

        // 延时1s执行任务
        mButtonStart = (Button) findViewById(R.id.btn_start);
        mButtonStart.postDelayed(new Runnable() {
            @Override
            public void run() {
                // 需要执行的延时任务
            }
        }, 1000);

3)timer+timetask;

        Timer timer = new Timer();
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                // 需要执行的延时任务
            }
        };
        // 延时1s执行任务
        timer.schedule(timerTask, 1000);

4)Thread.sleep

        Thread.sleep(1000);
        //需要执行的延时任务

不建议使用此方法,Thread.sleep是 Java 中用于让当前执行的线程暂停执行指定的时间,进入到阻塞状态的一个静态方法。如果在一个线程中调用 Thread.sleep 方法,这个线程就会进入到阻塞状态,导致这个线程不能执行任何代码,也不能响应任何事件,直到经过指定的时间后,这个线程才重新进入到可运行状态;如果在主线程(UI线程)中调用 Thread.sleep 方法,会导致应用程序出现卡顿现象,用户体验极差。

7.Android的启动模式
1)standard(标准模式)
这是默认的启动模式。每次启动一个Activity实例,都会在任务栈中新增一个Activity实例。

2)singleTop(栈顶复用模式)
如果新的Activity已经位于任务栈的顶部,那么此Activity不会创建新的实例,而是复用现有的Activity实例。

3)singleTask(栈内复用模式)
如果任务栈中已经有了该Activity的实例,那么此Activity上面的所有Activity实例都会被移除栈,使得这个Activity成为栈顶元素。

4)singleInstance(单例模式)
这是一种加强的singleTask模式,在这种模式下,一个Activity只会存在一个实例,并且这个实例会在一个新的任务栈中。

8.进程
1)分类:
①前台进程:该进程包含正在与用户进行交互的界面组件,如Activity。在接收关键生命周期方法时会让一个进程临时提升为前台进程,包括任何服务的生命周期方法onCreate()和onDestroy()和任何广播接收器onReceive()方法。这样做确保了这些组件的操作是有效的原子操作,每个组件都能执行完成而不被杀掉。
②可见进程:该进程中的组件虽然没有和用户交互,但是仍然可以被看到。activity可见的时候不一定在前台。比如前台的activity使用对话框启动一个新的activity或者一个透明activity 。另一个例子是当调用运行时权限对话框时(事实上它就是一个 activity)。
③服务进程:该进程包含在执行后台操作的服务组件,比如播放音乐的Service。对于许多在后台做处理而没有立即成为前台服务的应用都属于这种情况。
④后台进程:该进程包含的组件没有与用户交互,用户也看不到 Service。在一般操作场景下,设备上的许多内存就是用在这上面的,使可以重新回到之前打开过的某个 activity 。
⑤空进程:没有任何界面组件、服务组件,或触发器组件,只是出于缓存的目的而被保留(为了更加有效地使用内存而不是完全释放掉),只要 Android 需要可以随时杀掉它们。

2)启动方式:
在AndroidManifest.xml中注册Service、Activity、Receiber、ContentProvider时指定"android:process”属性。
①私有进程的名称前面有冒号,它属于当前应用的私有进程,其它应用的组件不能和它跑在同一进程中。
②全局进程的名称前面没有冒号。

3)引发的问题:
①静态成员和单例模式失效;
②线程同步机制失效;
③SharedPreferences可靠性降低;
④Application被多次创建;

4)进程间通信方式:
①Intent(Bundle) 
②ContentProvider
③文件
④广播Broadcast
⑤AIDL方式
⑥Messenger
Android 进程+线程

9.service的启动方式
1)startService
通过startService启动后,service会一直无限期运行下去。当外部调用了stopService()或stopSelf()方法时,该Service才会停止运行并销毁;当系统资源不足时, 会回收一些不重要的service,service被系统回收也会停止运行并被销毁。适用于Service需要在后台长时间运行的情况,适用于音乐播放、资源下载等。

生命周期:
①onCreate()
1.如果service没被创建过,调用startService()后会执行onCreate()回调;
2.如果service已处于运行中,调用startService()不会执行onCreate()方法。
此方法适合完成一些初始化工作。
②onStartCommand()
如果多次执行了Context的startService()方法,那么Service的onStartCommand()方法也会相应的多次调用,该方法中根据传入的Intent参数进行实际的操作。
③onBind()
Service中的onBind()方法是抽象方法,Service类本身就是抽象类,所以onBind()方法是必须重写的,即使我们用不到。
④onDestory()
在销毁的时候会执行Service该方法。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 启动Service
        Intent serviceIntent = new Intent(this, MyService.class);
        startService(serviceIntent);
    }

    public class MyService extends Service {
        @Override
        public void onCreate() {
            super.onCreate();
        }

        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            // 处理Service的逻辑
            return super.onStartCommand(intent, flags, startId);
        }

        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            // 如果Service需要被绑定,则返回一个IBinder实例
            return null;
        }

        @Override
        public void onDestroy() {
            super.onDestroy();
        }
    }

2)bindService
绑定Service会创建一个客户端-服务提供者的关系,当绑定的服务死亡时,系统会自动重启服务,当客户端销毁时,服务端会自动解除绑定,也可以手动通过unbindService()方法解除绑定。适用于需要与Service交互的场景,如传递数据、获取服务的实例等。

生命周期:
①onCreate()
当服务通过onStartCommand()和onBind()被第一次创建的时候,系统调用该方法。该调用要求执行一次性安装。
②onBind()
当其他组件想要通过bindService()来绑定服务时,系统调用该方法。如果你实现该方法,你需要返回IBinder对象来提供一个接口,以便客户来与服务通信。你必须实现该方法,如果你不允许绑定,则直接返回null。
③onUnbind()
客户中断所有服务发布的特殊接口时,系统调用该方法。
④onRebind()
当新的客户端与服务连接,且此前它已经通过onUnbind(Intent)通知断开连接时,系统调用该方法。
⑤onDestroy()
当服务不再有用或者被销毁时,系统调用该方法。你的服务需要实现该方法来清理任何资源,如线程,已注册的监听器,接收器等。

        // 绑定Service
        Intent serviceIntent = new Intent(this, MyService.class);
        connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                // 当Service连接成功时调用
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                // 当Service连接断开时调用
            }
        };
        bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE);

在执行绑定服务后,务必记得在onDestroy中及时解绑,避免导致内存泄漏。

    protected void onDestroy() {
        super.onDestroy();
        unbindService(connection);
    }

10.动画的分类
①帧动画
②补间动画
③属性动画
Android 动画详解

11.避免使用枚举的原因
①枚举会增加dex文件大小
②枚举会增加dex文件方法数量
③枚举会增加内存的使用
④枚举会增加字符串常量
⑤枚举会增加函数调用时间
android开发之避免使用枚举

12.SurfaceView的使用
SurfaceView是继承自View的一个特殊视图,它可以在一个独立的线程中绘制图像。SurfaceView通过创建一个叫做Surface的窗口来实现图像的显示,这个Surface可以在一个新的子线程中进行绘制操作,从而避免了主线程被占用而导致的UI卡顿。

SurfaceView的主要特点:
① 可以在独立的线程中进行绘制操作,避免主线程的阻塞;
② 适用于实时更新图像的场景,如使用Camera预览、播放视频等;
③ 可以通过getHolder()方法获取SurfaceHolder对象,进而进行绘制操作。
安卓SurfaceView+HandlerThread的使用

13.HandlerThread的使用
在安卓开发中,如果需要执行耗时操作,则可以开启子线程来完成,然而手动创建销毁线程又麻烦又消耗系统性能,因此可以使用线程池来完成。如果还需要在线程中使用Handler异步消息机制,或者需要实现子线程和子线程之间的通讯(Handler是主线程和子线程之间的通讯),那么就可以用HandlerThreaad。

HandlerThread是Google封装好的一个类,它的内部有自己的Looper对象,可以进行Loop轮询,用于执行多个耗时操作,而不需要多次开启线程,本质是使用Handler和Looper实现的。

HandlerThread的主要特点:
① HandlerThread本质上是一个线程类,它继承了Thread;
② HandlerThread有自己的内部Looper对象,可以进行looper循环;
③ 通过获取HandlerThread的looper对象传递给Handler对象,可以在handleMessage方法中执行异步任务;
④ 创建HandlerThread后必须先调用HandlerThread.start()方法,Thread会先调用run方法,创建Looper对象。

14.Fragment的启动方式
① 在xml布局文件绑定Fragment

        // 1.获取FragmentManager,在活动中可以直接通过调用getFragmentManager()方法得到
        FragmentManager fragmentManager = getSupportFragmentManager();
        // 2.开启一个事务,通过调用beginTransaction()方法开启
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        // 3.向容器内添加或替换碎片,一般使用replace()方法实现,需要传入容器的id和待添加的碎片实例
        transaction.replace(R.id.fr_container, fragment);
        // 4.使用addToBackStack()方法,将事务添加到返回栈中,填入的是用于描述返回栈的一个名字
        transaction.addToBackStack(null);
        // 5.提交事物,调用commit()方法来完成
        transaction.commit();

建议使用此方法。

15. BroadcastReceiver广播的使用
BroadcastReceiver全面解析

16.Context的理解
● Context也叫上下文,是有关应用程序的环境的全局信息的接口。这是一个抽象类,它允许访问特定应用程序的资源和类,以及对应用程序级操作的调用,比如启动活动、发送广播、接收意图等;Activity、Service、Application都是Context的子类。
● Context的具体实现类是ContextImpl,还有一个包装类ContextWrapper,ContextWrapper的子类有Service、Application、ContextThemeWrapper等,Activity又是ContextThemeWrapper的子类,ContextThemeWrapper又叫UI Context,与UI相关操作最好使用此类Context。
● ContextThemeWrapper中有一个mBase,这个mBase就是ContextImpl,它在Activity、Service、Application创建时通过attachBaseContext()将各自对应的ContextImpl赋值,对于Context的操作最终都是ContextImpl实现的。
● Activity中ContextImpl的创建时机:
应用启动,系统启动进程。系统创建ActivityThread类的实例,开始进入消息循环。当Activity启动时,ActivityThread会调用Activity的performLaunchActivity()方法,并创建一个ContextImpl对象,然后通过Activity的attach方法将ContextImpl设置给Activity,而Activity中attach()方法则会调用attachBaseContext()方法。
● 对于startActivity的操作:
① 当为Activity的Context时,则可直接使用。
② 当为其它Context,则必须带上FLAG_ACTIVITY_NEW_TASK才能使用,因为非Activity的的Context启动Activity没有Activity栈,则无法启动,因此需要开启新的栈。

17.Service和IntentService
● Service是长期运行在后台的应用程序组件 。它不是一个单独的进程,它和应用程序在同一个进程中。Service 也不是一个线程,它和线程没有任何关系,所以它不能直接处理耗时操作。如果直接把耗时操作放在 Service 的 onStartCommand() 中,很容易引起 ANR .如果有耗时操作就必须开启一个单独的线程来处理。例如后台音乐播放。
● IntentService 是继承于 Service 并处理异步请求的一个类,在 IntentService 内有一个工作线程来处理耗时操作,
启动 IntentService 的方式和启动传统 Service 一样,同时,当任务执行完后, IntentService 会自动停止 ,而不需要我们去手动控制。
另外,可以启动 IntentService 多次,而每一个耗时操作会以工作队列的方式在IntentService 的 onHandleIntent 回调方法中执行,
并且,每次只会执行一个工作线程,执行完第一个再执行第二个, 有序执行。例如下载资源。

  • 29
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值