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文件 (注意:一定要是全路径[包名+文件名])
(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>
@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 单线程化的线程池,他确保了所有的任务都在一个线程中执行,不存在线程同步的问题。