Andorid相关基础笔记

1.AsyncTask用法

(1).onPreExecute()
在UI thread调用,可以在该方法中做一些准备工作,如在界面上显示一个进度条。
(2).doInBackground(Params...)
主要是执行那些很耗时的后台计算工作,通过调用 publishProgress方法来更新实时的任务进度。(必须要实现的方法)
(3).onProgressUpdate()
调用 publishProgress(downloadPercent)触发该方法。
(4).onPostExecute()
在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.

public class DownLoadTask extends AsyncTask<Void ,Integer,Boolean> {

    @Override
    protected void onPreExecute() {
        progressDialog.show();
    }
    @Override
    protected Boolean doInBackground(Void... params)     {
        try{
            while(true) {
                int downloadPercent = doDownload();
                publishProgress(downloadPercent);
                if(downloadPercent >=100){
                    break;
                }
            }
        } catch(Exception e){
            return false;}
        return true;
    }
    @Override
    protected void onProgressUpdate(Integer...values) {
        progressDialog.setMessage("当前下载进度:"+values[0]+"%");
    }

    @Override
    protected void onPostExecute(Boolean result) {
        progressDialog.dismiss();
        if(result) {
            Toast.makeText(context, "下载成功",Toast.LENGTH_SHORT).show();
        }else {
            Toast.makeText(context, "下载失败",Toast.LENGTH_SHORT).show();
        }
    }
}

2.JNI用法

(1).新建jni文件(一个静态的带native关键字的方法,并且使用静态代码块加载该library)

public class JniTest {
    private JniTest(){}
    public static JniTest instatnce=null;
    public static JniTest getInstatnce(){
        if(instatnce==null){
            instatnce=new JniTest();
        }
        return instatnce;
    }
   static {
        System.loadLibrary("JniTest");//jni模块加载名称
    }
    public static native String getJniString();//红色的本地方法,暂时不用理会
}

(2).build---->make project 生成JniTest.class文件(app-build-intermediatges-classes-debug-包名-JniTest.class)

(3).打开Termial终端,cd到debug目录,执行javah -jni com.example.jianshui.firstndk.JniTest ,生成.h文件 (注意:一定要是全路径[包名+文件名])


image.png

(4).新建一个jni的文件夹,然后在jni文件夹下面新建一个.c文件夹,将.h文件的内容拷贝到拷贝到.c文件夹中,并实现里面的抽象方法。

(5).在build文件中配置一下,然后gradle一下,就会在app-build-intermediages下面生成7个so库

  sourceSets {
        main {
            jni.srcDirs = ['src/main/jni']
        }
    }

(6).使用

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String testStr=new JniTest().getJniString();
        Toast.makeText(getApplicationContext(),testStr,Toast.LENGTH_LONG).show();
    }
}

3.Handler ,Message,Looper之间的关系(消息传递机制)(https://www.cnblogs.com/yishaochu/p/6882387.html)

(1).Handler 发送消息,接收消息

 Message msg = netHandler.obtainMessage();
                    msg.what = Constant.MSG_REQUEST_SUCCESS;
                    msg.obj = moreListBean;
                    netHandler.sendMessage(msg);
 Handler netHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case Constant.MSG_REQUEST_SUCCESS:
                    break;
            }
        }
    };

当我们new一个Handler对象的时候,他的构造方法持有mLooper ,和mLooper.mQueue,
当我们调用sendMessage--->sendMessageDelayed-->sendMessageAtTime-->enqueueMessage(queue, msg, uptimeMillis) 此时消息别注入到 Loop所在的MessageQueue队列中.

public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

(2).Looper 创建MessageQueue对象,提供了loop()方法不断的死循环,从MessageQueue中遍历消息
(2-1).创建MessageQueue对象

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

(2-2).提供了loop()方法不断的死循环,从MessageQueue中遍历消息,并且调用 msg.target.dispatchMessage(msg);msg.target也就是Handler,并且调用dispatchMessage方法

public static void loop() {
    final Looper me = myLooper();//获取Looper对象
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;//从Looper对象获取MessageQueue对象

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();
    for (;;) {//死循环  一直从MessageQueue中遍历消息
        Message msg = queue.next(); // might block
        if (msg == null) {
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        final long traceTag = me.mTraceTag;
        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        try {
            //调用handler的dispatchMessage方法,把消息交给handler处理
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }

        msg.recycleUnchecked();
    }
}
其实就是一个死循环,一直会从MessageQueue中取消息,如果取到了消息呢,会执行msg.target.dispatchMessage(msg)这行代码,msg.target就是handler,其实就是调用handler的dispatchMessage方法,然后把从MessageQueue中取到的message传入进去。
public void dispatchMessage(Message msg) {
    //如果callback不为空,说明发送消息的时候是post一个Runnable对象
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {//这个是用来拦截消息的
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);//最终调用我们重写的handleMessage方法
    }
}

(3).ActivityThread main ()

ActivityThread类是Android APP进程的初始类,它的main函数是这个APP进程的入口。我们看看这个main函数干了什么事情。

public static final void main(String[] args) {
  
        Looper.prepareMainLooper();
        if (sMainThreadHandler == null) {
            sMainThreadHandler = new Handler();
        }

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        Looper.loop();
      
}
注意:在第二行代码调用Looper.prepareMainLooper()方法,第13行调用了Looper.loop()方法,也就是说,app在启动的时候,loop方法中的死循环就开始了。
总结:Handler持有looper和looper.queue对象,负责发送消息,然后把消息放入looper.queue,然后Looper提供创建了MessageQueue,并提供了loop方法,不断的for循环消息,如果有消息,就调用msg.target.dispathMessage(),然后回调handler中的handlermessage方法处理消息。loop()方法无限循环的调用者是ActivityThread在初始化的时候调用启动的。

4.自定义View的基本原理(http://blog.51cto.com/ticktick/1540134)

(1).构造方法

// 如果View是在Java代码里面new的,则调用第一个构造函数
 public CarsonView(Context context) {
        super(context);
    }

// 如果View是在.xml里声明的,则调用第二个构造函数
// 自定义属性是从AttributeSet参数传进来的
    public  CarsonView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

// 不会自动调用
// 一般是在第二个构造函数里主动调用
// 如View有style属性时
    public  CarsonView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    //API21之后才使用
    // 不会自动调用
    // 一般是在第二个构造函数里主动调用
    // 如View有style属性时
    public  CarsonView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

(2).onMeassure
应用场景:http://blog.51cto.com/ticktick/1540134
(2-1).specMode ==MeasureSpec.EXACTLY
A:如果View的layout给出了确定的值,比如100dp
B:或者View使用的是match_parent,但是父控件的Size已经可以确定了,比如设置的是具体的值或者match_parent
(2-2).specMode == MeasureSpec.AT_MOST
A:如果View的layout使用的是warp_content
B:如果View的layout使用的是match_parent但是父控件使用的是不确定的值,比如wrap_content
(2-3).specMode == MeasureSpec.UNSPECIFIED
UNSPECIFIED,没有任何限制,所以可以设置任何大小
多半出现在自定义的父控件的情况下,期望由子控件自行决定大小
应用场景:https://blog.csdn.net/u012947056/article/details/81292621

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            xmlns:tools="http://schemas.android.com/tools"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:orientation="vertical">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:background="@color/gray"
        android:hint="haha"
        />
</ScrollView>
image.png
 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        
        int width  = measureDimension(DEFAULT_VIEW_WIDTH, widthMeasureSpec);
        int height = measureDimension(DEFAULT_VIEW_HEIGHT, heightMeasureSpec);
        
        setMeasuredDimension(width, height);                
    }
    
    protected int measureDimension( int defaultSize, int measureSpec ) {
        
        int result = defaultSize;
        
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
                
        //1. layout给出了确定的值,比如:100dp
        //2. layout使用的是match_parent,但父控件的size已经可以确定了,比如设置的是具体的值或者match_parent
        if (specMode == MeasureSpec.EXACTLY) {      
            result = specSize; //建议:result直接使用确定值
        } 
        //1. layout使用的是wrap_content
        //2. layout使用的是match_parent,但父控件使用的是确定的值或者wrap_content
        else if (specMode == MeasureSpec.AT_MOST) {             
            result = Math.min(defaultSize, specSize); //建议:result不能大于specSize
        } 
        //UNSPECIFIED,没有任何限制,所以可以设置任何大小
        //多半出现在自定义的父控件的情况下,期望由自控件自行决定大小
        else {      
            result = defaultSize; 
        }
        
        return result;
    }

(3).ondraw() 和dispatchdraw()的区别

绘制VIew本身的内容,通过调用View.onDraw(canvas)函数实现
绘制自己的孩子通过dispatchDraw(canvas)实现
View组件的绘制会调用draw(Canvas canvas)方法,draw过程中主要是先画Drawable背景,对 drawable调用setBounds()然后是draw(Canvas c)方法.

(4).invalidate()和postInvalidate() 区别
在主线程中重画:要重画的时候调用invalidate(),就会重新调用onDraw方法
在子线程中重画:postInvalidate()

注意:调用这二个方法会触发onDraw()

requestLayout(),触发onLayout方法

参考文章:https://www.cnblogs.com/huolongluo/p/6017917.html

5.Android进程通信(Inter-Process Communication)简称IPC通信

(1).Activity可以跨进程调用其他应用程序的Activity;
打电话

 Intent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:12345678"); 
 startActivity(callIntent);

(2)ContentProvider可以跨进程访问其他应用程序中的数据(以Cursor对象形式返回),
当然,也可以对其他应用程序的数据进行增、删、改操作;
Android系统本身提供了很多Content Provider,例如,音频、视频、联系人信息等等。我们可以通过这些Content Provider获得相关信息的列表。这些列表数据将以Cursor对象返回。因此,从Content Provider返回的数据是二维表的形式。

(3).Broadcast可以向android系统中所有应用程序发送广播,
而需要跨进程通讯的应用程序可以监听这些广播;
广播是一种被动跨进程通讯的方式。当某个程序向系统发送广播时,其他的应用程序只能被动地接收广播数据。这就象电台进行广播一样,听众只能被动地收听,而不能主动与电台进行沟通。

(4)Service这种可以跨进程通讯的服务叫AIDL服务。
注意普通的Service并不能实现跨进程操作,实际上普通的Service和它所在的应用处于同一个进程中,而且它也不会专门开一条新的线程,因此如果在普通的Service中实现在耗时的任务,需要新开线程。
要实现跨进程通信,需要借助AIDL(Android Interface Definition Language)。Android中的跨进程服务其实是采用C/S的架构,因而AIDL的目的就是实现通信接口。

6.Android进程通信(Inter-Process Communication)简称IPC通信

(1).继承Thread类

public class CustomThread extends Thread{
    @Override
    public void run() {
        // 处理耗时逻辑
    }
}

启动线程:

new CustomThread().start(); 

(2). 实现 Runnable 接口
一般采用此方法实现多线程,这样耦合度更低,而且可以实现类的扩展性更好,因为 Java 类支持实现多接口。

public class CustomThread implements Runnable{
    @Override
    public void run() {
        // 处理耗时逻辑
    }
}

启动线程:
new Thread(new CustomThread()).start();

(3).线程池 https://www.cnblogs.com/lanseyitai1224/p/7895652.html

Executors.newFixedThreadPool 定长线程池,可以控制最大并发数,超出的线程会在队列中等待

Executors.newCachedThreadPool 可缓存的线程池,如果线程池长度超多处理的需要,可以灵活回收空闲线程,
如果没有可以回收的,也可新建线程。

Executors.newScheduledThreadPool 带有周期性质的定长线程线程,支持定时和周期任务的执行

Executors.newSingleThreadExecutor 单线程化的线程池,他确保了所有的任务都在一个线程中执行,不存在线程同步的问题。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容