您的位置:首页 > 移动开发 > Android开发

Android的一些异常汇总

2015-09-02 17:55 253 查看
本片文章出自http://blog.csdn.net/andywuchuanlong,转载请说明出处,谢谢!

之前遇到同行问的一些问题,能记住的我都将其写出来,供初学者参考。问题如下:

1、四大组件全部结束销毁,为什么应用依然在后台运行?为什么不能真正的退出应用?

2、Android中的service是在后台运行的服务,貌似线程也是在后台异步执行,为什么service不能被线程替代?

3、使用View.inflate(context, resource, root)加载布局文件的时候,如果root为null,为什么布局文件的根节点设置的属性例如外边距、高度等都不起作用?

4、网络请求的时候,我们都需要开启线程,那么是使用asyncTask还是使用Thread+Handler模式呢?

5、为什么ScrollView中嵌套了ViewPager后,viewPager高度出现问题并且滑动失效?

6、在项目中美工一般给我们切几套图,才能够适配Android碎片化的终端?

7、Bitmap内存回收问题

8、java语言是与平台无关的,项目中为了保证一些固定数据或者算法的安全性,我们一般会使用jni技术将其写在c里面,然而jni是与具体平台有关的,那么这中方式是不是违背了java语言的跨平台的特性?

9、listview中的convertView复用过程是怎样的,它一定会被复用吗?

10、Android中的ipc机制和aidl机制是什么关系?项目中什么情况下会使用到aidl?

11、service、binder和aidl有什么联系?

以上这些问题稍后我都会单独以文章的形式写出来,欢迎大家前来喷水交流。

本片文章出自http://blog.csdn.net/andywuchuanlong,转载请说明出处,谢谢!

我答同行问序列目录http://blog.csdn.net/andywuchuanlong/article/details/44194043

1、四大组件全部结束销毁,为什么应用依然在后台运行?为什么不能真正的退出应用?

2、Android中的service是在后台运行的服务,貌似线程也是在后台异步执行,为什么service不能被线程替代?

想要回答第一个问题就需要扯到Android内存管理机制。Android内存的管理有自己的一套机制,它有运行时和自己的虚拟机来管理自己的进程。java应用程序,程序代码运行结束,这个进程就会跟着销毁,而Android程序,就算所有的程序代码运行完毕,所在的进程也不见得会被销毁终止,也就是说Android有它自己管理进程生命周期的一套方式。换句话说Android系统可以自己控制进程的生命周期。当系统在遭遇内存瓶颈的时候,会通过杀死或者停止一些优先级比较低的进程,从而释放内存保证其他进程的运行,但是当系统内存足够的情况下,就算是一个空进程,系统也有可能不会对这个空进程下毒手,因为Android为了优化应用下一次的启动速度,这些比前台进程优先级低的会被缓存在内存中。

上面提到了前台进程和空进程的概念,在Android中有五种进程的概念,优先级依次降低

1、前台进程:一个用户当前工作所需要的,例如:

当有一个Activity正与用户交互,onResume方法被调用,广播在执行onReceive方法,service在执行onCreate等方法,这个应用所在进程就可以视为前台进程,也称为活动进程。

2、可视进程

没有任何前台组件的进程,但是却是用户可以看到的,比如说Activity的onPause方法被调用,此时界面上呈现的是一个对话框

3、服务进程

用startService()启动的服务,但是不满足前面两类

4、后台进程

一个保存着当前用户不可视的进程,Activity的onStop被调用

5、空进程

是一个没有保持活跃的应用程序组件的进程。保持这个进

程可用的唯一原因是作为一个

cache

以提高下次启动组件的速度

是一个没有保持活跃的应用程序组件的进程。保持这个进

程可用的唯一原因是作为一个

cache

以提高下次启动组件的速度

是一个没有保持活跃的应用程序组件的进程。保持这个进

程可用的唯一原因是作为一个

cache

以提高下次启动组件的速度
不符合以上的条件的进程,通常来说空进程的存在是为了缓存在内存中以保证下一次的启动速度。

第二个问题涉及到的就是上面所述的进程问题

应用中的线程是由应用进程派生出来的,当这个进程从可视进程变为后台进程的时候,系统可能由于内存的压力会将其销毁,那么这些线程也会跟着销毁。然后service所在的进程会被作为一个服务进程运行在后台,从优先级可知,服务进程的优先级优先于后台进程,当内存不足时,后台进程会首先被销毁。

3、使用View.inflate(context, resource, root)加载布局文件的时候,如果root为null,为什么布局文件的根节点设置的属性例如外边距、高度等都不起作用?

分析这个问题的时候需要从远源码进行分析了,假设参数root为null,inflater.inflate(R.layout.item_list,
parent,false);调用方式如下:

[java] view
plaincopy

<span style="color: rgb(51, 51, 51);">public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {</span><span style="color:#ff0000;">// attachToRoot false</span><span style="color:#333333;">

synchronized (mConstructorArgs) {

final AttributeSet attrs = Xml.asAttributeSet(parser);

Context lastContext = (Context)mConstructorArgs[0];

mConstructorArgs[0] = mContext;

</span><span style="color:#ff0000;"> View result = root;// 其实是resutl = null</span><span style="color:#333333;">

try {

// Look for the root node.

int type;

while ((type = parser.next()) != XmlPullParser.START_TAG &&

type != XmlPullParser.END_DOCUMENT) {

// Empty

}

if (type != XmlPullParser.START_TAG) {

throw new InflateException(parser.getPositionDescription() + ": No start tag found!");

}

final String name = parser.getName();

if (TAG_MERGE.equals(name)) {

if (root == null || !attachToRoot) {

throw new InflateException("<merge /> can be used only with a valid "

+ "ViewGroup root and attachToRoot=true");

}

rInflate(parser, root, attrs, false);

} else {

// Temp is the root view that was found in the xml

View temp;

if (TAG_1995.equals(name)) {

temp = new BlinkLayout(mContext, attrs);

} else {

temp = createViewFromTag(root, name, attrs);

}

ViewGroup.LayoutParams params = null;

if (root != null) {

if (DEBUG) {

System.out.println("Creating params from root: " +

root);

}

// Create layout params that match root, if supplied

params = root.generateLayoutParams(attrs);

if (!attachToRoot) { </span><span style="color:#ff0000;">// true</span><span style="color:#333333;">

// Set the layout params for temp if we are not

// attaching. (If we are, we use addView, below)

temp.setLayoutParams(params);

}

}

if (DEBUG) {

System.out.println("-----> start inflating children");

}

// Inflate all children under temp

rInflate(parser, temp, attrs, true);

if (DEBUG) {

System.out.println("-----> done inflating children");

}

// We are supposed to attach all the views we found (int temp)

// to root. Do that now.

if (root != null && attachToRoot) {

root.addView(temp, params);

}

// Decide whether to return the root that was passed in or the

// top view found in xml.

if (root == null || !attachToRoot) {

result = temp;

}

}

} catch (XmlPullParserException e) {

InflateException ex = new InflateException(e.getMessage());

ex.initCause(e);

throw ex;

} catch (IOException e) {

InflateException ex = new InflateException(

parser.getPositionDescription()

+ ": " + e.getMessage());

ex.initCause(e);

throw ex;

} finally {

// Don't retain static reference on context.

mConstructorArgs[0] = lastContext;

mConstructorArgs[1] = null;

}

return result;

}

}</span>

代码比较长,我们重点关注下面的代码

<span class="keyword" style="font-weight: bold;">if</span> (root != <span class="keyword" style="font-weight: bold;">null</span>) {
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="keyword" style="font-weight: bold;">if</span> (DEBUG) {
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span>    System.<span class="keyword" style="font-weight: bold;">out</span>.println(<span class="string" style="color: rgb(221, 17, 68);">"Creating params from root: "</span> +
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="indent">  </span>    root);
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span>}
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">// Create layout params that match root, if supplied</span>
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="keyword" style="font-weight: bold;">params</span> = root.generateLayoutParams(attrs);
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span><span class="keyword" style="font-weight: bold;">if</span> (!attachToRoot) {
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span>    <span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">// Set the layout params for temp if we are not</span>
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span>    <span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">// attaching. (If we are, we use addView, below)</span>
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span>    temp.setLayoutParams(<span class="keyword" style="font-weight: bold;">params</span>);
<span class="indent">  </span><span class="indent">  </span><span class="indent">  </span>}
<span class="indent">  </span><span class="indent">  </span>    }
这些代码的意思就是,当我们传进来的root参数不是空的时候,并且attachToRoot是false的时候,也就是上面的TwoActivity的实现方式的时候,会给temp设置一个LayoutParams参数。那么这个temp又是干嘛的呢?
<pre name=<span class="string" style="color: rgb(221, 17, 68);">"code"</span> <span class="keyword" style="font-weight: bold;">class</span>=<span class="string" style="color: rgb(221, 17, 68);">"java"</span>><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">// We are supposed to attach all the views we found (int temp)</span>
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">// to root. Do that now.</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">if</span> (root != <span class="keyword" style="font-weight: bold;">null</span> && attachToRoot) {
<span class="indent">  </span>    root.addView(temp, <span class="keyword" style="font-weight: bold;">params</span>);
<span class="indent">  </span>}

<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">// Decide whether to return the root that was passed in or the</span>
<span class="indent">  </span><span class="comment" style="color: rgb(153, 153, 136); font-style: italic;">// top view found in xml.</span>
<span class="indent">  </span><span class="keyword" style="font-weight: bold;">if</span> (root == <span class="keyword" style="font-weight: bold;">null</span> || !attachToRoot) {
<span class="indent">  </span>    result = temp;
<span class="indent">  </span>}
现在应该明白了吧,当我们传进来的root不是null,并且第三个参数是false的时候,这个temp就被加入到了root中,并且把root当作最终的返回值返回了。而当我们设置root为空的时候,没有设置 LayoutParams参数的temp对象,作为返回值返回了。

因此,我们可以得出下面的结论:

1.若我们采用 convertView = inflater.inflate(R.layout.item_list, null);方式填充视图,item布局中的根视图的layout_XX属性会被忽略掉,然后设置成默认的包裹内容方式

2.如果我们想保证item的视图中的参数不被改变,我们需要使用convertView = inflater.inflate(R.layout.item_list, parent,false);这种方式进行视图的填充

3.除了使用这种方式,我们还可以设置item布局的根视图为包裹内容,然后设置内部控件的高度等属性,这样就不会修改显示方式了。
简单总结一下:

当root为null的时候,我们只是把一个xml文件实例化成view对象,返回的就是这个xml对应的view。当root不为空的时候,也就是parent存在,则将实例化后的view对象添加进parent中,然后返回

4、网络请求的时候,我们都需要开启线程,那么是使用asyncTask还是使用Thread+Handler模式呢?

网络请求是每个app都需要进行的,很多人会使用asyncTask,也有人喜欢Thread+Handler,下面我按照我的想法讲解一下他们两者的区别。


1、asyncTask

asyncTask本质上也是一个线程池,2.3之前默认最多同时执行5个任务,是并行执行任务的。而3.0之后默认是串行执行,也就是说只有一个任务在执行,下面主要讲解下3.0之后的asyncTask。

AsyncTask中维护者一个静态并发线程池,可以用来并行执行任务,尽管从3,0开始,AsyncTask默认是串行执行任务

但是我们仍然能构造出并行的AsyncTask。可能有人觉得奇怪了,明明维护者一个并发线程池,怎么说是串行执行的呢,主要还是AsyncTask维护了一个静态串行任务执行器其内部实现了串行控制,当任务开始执行的时候,这个task会被加入到任务队列中,然后从队列中循环的取出一个个的任务交给并发线程池去执行,这个任务执行器就是3.0之后默认的执行器,如果我们要改为并行执行把这个默认的执行器替换掉就可以了。任务执行完毕之后,会通过AsyncTask内部的一个handler发送消息到主线程,这里也就说明了为什么AsyncTask只能在ui线程中创建,因为AsyncTask内部维护了一个Handler,而消息时有Looper来循环的,子线程中默认是没有Looper的。

[java] view
plaincopy

public abstract class AsyncTask<Params, Progress, Result> {

private static final String LOG_TAG = "AsyncTask";

//获取当前的cpu核心数

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();

//线程池核心容量

private static final int CORE_POOL_SIZE = CPU_COUNT + 1;

//线程池最大容量

private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;

private static final int KEEP_ALIVE = 1;

//ThreadFactory 线程工厂,通过工厂方法newThread来获取新线程

private static final ThreadFactory sThreadFactory = new ThreadFactory() {

//原子整数,可以在超高并发下正常工作

private final AtomicInteger mCount = new AtomicInteger(1);

public Thread newThread(Runnable r) {

return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());

}

};

//静态阻塞式队列,用来存放待执行的任务,初始容量:128个

private static final BlockingQueue<Runnable> sPoolWorkQueue =

new LinkedBlockingQueue<Runnable>(128);

/**

* 静态并发线程池,可以用来并行执行任务,尽管从3.0开始,AsyncTask默认是串行执行任务

* 但是我们仍然能构造出并行的AsyncTask

*/

public static final Executor THREAD_POOL_EXECUTOR

= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,

TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

/**

* 静态串行任务执行器,其内部实现了串行控制,

* 循环的取出一个个任务交给上述的并发线程池去执行

*/

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

//消息类型:发送结果

private static final int MESSAGE_POST_RESULT = 0x1;

//消息类型:更新进度

private static final int MESSAGE_POST_PROGRESS = 0x2;

/**静态Handler,用来发送上述两种通知,采用UI线程的Looper来处理消息

* 这就是为什么AsyncTask必须在UI线程调用,因为子线程

* 默认没有Looper无法创建下面的Handler,程序会直接Crash

*/

private static final InternalHandler sHandler = new InternalHandler();

//默认任务执行器,被赋值为串行任务执行器,就是它,AsyncTask变成串行的了

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

private final WorkerRunnable<Params, Result> mWorker;

private final FutureTask<Result> mFuture;

private volatile Status mStatus = Status.PENDING;

private final AtomicBoolean mCancelled = new AtomicBoolean();

private final AtomicBoolean mTaskInvoked = new AtomicBoolean();

/**串行执行器的实现,我们要好好看看,它是怎么把并行转为串行的

*目前我们需要知道,asyncTask.execute(Params ...)实际上会调用

*SerialExecutor的execute方法,这一点后面再说明。也就是说:当你的asyncTask执行的时候,

*首先你的task会被加入到任务队列,然后排队,一个个执行

*/

private static class SerialExecutor implements Executor {

//线性双向队列,用来存储所有的AsyncTask任务

final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();

//当前正在执行的AsyncTask任务

Runnable mActive;

public synchronized void execute(final Runnable r) {

//将新的AsyncTask任务加入到双向队列中

mTasks.offer(new Runnable() {

public void run() {

try {

//执行AsyncTask任务

r.run();

} finally {

//当前AsyncTask任务执行完毕后,进行下一轮执行,如果还有未执行任务的话

//这一点很明显体现了AsyncTask是串行执行任务的,总是一个任务执行完毕才会执行下一个任务

scheduleNext();

}

}

});

if (mActive == null) {

//如果当前没有任务在执行,直接进入执行逻辑

scheduleNext();

}

}

protected synchronized void scheduleNext() {

if ((mActive = mTasks.poll()) != null) {

//从任务队列中取出队列头部的任务,如果有就交给并发线程池去执行

THREAD_POOL_EXECUTOR.execute(mActive);

}

}

}

/**

* Indicates the current status of the task. Each status will be set only once

* during the lifetime of a task.

*/

/**

* 任务的三种状态

*/

public enum Status {

/** 任务等待执行

* Indicates that the task has not been executed yet.

*/

PENDING,

/**任务正在执行

* Indicates that the task is running.

*/

RUNNING,

/**任务已经执行结束

* Indicates that {@link AsyncTask#onPostExecute} has finished.

*/

FINISHED,

}

/** @hide Used to force static handler to be created.

* 隐藏API:在UI线程中调用,用来初始化Handler

* */

public static void init() {

// sHandler.getLooper();

}

/** @hide 为AsyncTask设置默认执行器*/

public static void setDefaultExecutor(Executor exec) {

sDefaultExecutor = exec;

}

/**

* Creates a new asynchronous task. This constructor must be invoked on the UI thread.

*/

public AsyncTask() {

mWorker = new WorkerRunnable<Params, Result>() {

public Result call() throws Exception {

mTaskInvoked.set(true);

Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

//noinspection unchecked

return postResult(doInBackground(mParams));

}

};

mFuture = new FutureTask<Result>(mWorker) {

@Override

protected void done() {

try {

postResultIfNotInvoked(get());

} catch (InterruptedException e) {

android.util.Log.w(LOG_TAG, e);

} catch (ExecutionException e) {

throw new RuntimeException("An error occured while executing doInBackground()",

e.getCause());

} catch (CancellationException e) {

postResultIfNotInvoked(null);

}

}

};

}

private void postResultIfNotInvoked(Result result) {

final boolean wasTaskInvoked = mTaskInvoked.get();

if (!wasTaskInvoked) {

postResult(result);

}

}

private Result postResult(Result result) {

@SuppressWarnings("unchecked")

Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,

new AsyncTaskResult<Result>(this, result));

message.sendToTarget();

return result;

}

/**

* Returns the current status of this task.

*

* @return The current status.

*/

public final Status getStatus() {

return mStatus;

}

/**

* Override this method to perform a computation on a background thread. The

* specified parameters are the parameters passed to {@link #execute}

* by the caller of this task.

*

* This method can call {@link #publishProgress} to publish updates

* on the UI thread.

*

* @param params The parameters of the task.

*

* @return A result, defined by the subclass of this task.

*

* @see #onPreExecute()

* @see #onPostExecute

* @see #publishProgress

*/

protected abstract Result doInBackground(Params... params);

/**

* Runs on the UI thread before {@link #doInBackground}.

*

* @see #onPostExecute

* @see #doInBackground

*/

protected void onPreExecute() {

}

/**

* <p>Runs on the UI thread after {@link #doInBackground}. The

* specified result is the value returned by {@link #doInBackground}.</p>

*

* <p>This method won't be invoked if the task was cancelled.</p>

*

* @param result The result of the operation computed by {@link #doInBackground}.

*

* @see #onPreExecute

* @see #doInBackground

* @see #onCancelled(Object)

*/

@SuppressWarnings({"UnusedDeclaration"})

protected void onPostExecute(Result result) {

}

/**

* Runs on the UI thread after {@link #publishProgress} is invoked.

* The specified values are the values passed to {@link #publishProgress}.

*

* @param values The values indicating progress.

*

* @see #publishProgress

* @see #doInBackground

*/

@SuppressWarnings({"UnusedDeclaration"})

protected void onProgressUpdate(Progress... values) {

}

/**

* <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and

* {@link #doInBackground(Object[])} has finished.</p>

*

* <p>The default implementation simply invokes {@link #onCancelled()} and

* ignores the result. If you write your own implementation, do not call

* <code>super.onCancelled(result)</code>.</p>

*

* @param result The result, if any, computed in

* {@link #doInBackground(Object[])}, can be null

*

* @see #cancel(boolean)

* @see #isCancelled()

*/

@SuppressWarnings({"UnusedParameters"})

protected void onCancelled(Result result) {

onCancelled();

}

/**

* <p>Applications should preferably override {@link #onCancelled(Object)}.

* This method is invoked by the default implementation of

* {@link #onCancelled(Object)}.</p>

*

* <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and

* {@link #doInBackground(Object[])} has finished.</p>

*

* @see #onCancelled(Object)

* @see #cancel(boolean)

* @see #isCancelled()

*/

protected void onCancelled() {

}

/**

* Returns <tt>true</tt> if this task was cancelled before it completed

* normally. If you are calling {@link #cancel(boolean)} on the task,

* the value returned by this method should be checked periodically from

* {@link #doInBackground(Object[])} to end the task as soon as possible.

*

* @return <tt>true</tt> if task was cancelled before it completed

*

* @see #cancel(boolean)

*/

public final boolean isCancelled() {

return mCancelled.get();

}

/**

* <p>Attempts to cancel execution of this task. This attempt will

* fail if the task has already completed, already been cancelled,

* or could not be cancelled for some other reason. If successful,

* and this task has not started when <tt>cancel</tt> is called,

* this task should never run. If the task has already started,

* then the <tt>mayInterruptIfRunning</tt> parameter determines

* whether the thread executing this task should be interrupted in

* an attempt to stop the task.</p>

*

* <p>Calling this method will result in {@link #onCancelled(Object)} being

* invoked on the UI thread after {@link #doInBackground(Object[])}

* returns. Calling this method guarantees that {@link #onPostExecute(Object)}

* is never invoked. After invoking this method, you should check the

* value returned by {@link #isCancelled()} periodically from

* {@link #doInBackground(Object[])} to finish the task as early as

* possible.</p>

*

* @param mayInterruptIfRunning <tt>true</tt> if the thread executing this

* task should be interrupted; otherwise, in-progress tasks are allowed

* to complete.

*

* @return <tt>false</tt> if the task could not be cancelled,

* typically because it has already completed normally;

* <tt>true</tt> otherwise

*

* @see #isCancelled()

* @see #onCancelled(Object)

*/

public final boolean cancel(boolean mayInterruptIfRunning) {

mCancelled.set(true);

return mFuture.cancel(mayInterruptIfRunning);

}

/**

* Waits if necessary for the computation to complete, and then

* retrieves its result.

*

* @return The computed result.

*

* @throws CancellationException If the computation was cancelled.

* @throws ExecutionException If the computation threw an exception.

* @throws InterruptedException If the current thread was interrupted

* while waiting.

*/

public final Result get() throws InterruptedException, ExecutionException {

return mFuture.get();

}

/**

* Waits if necessary for at most the given time for the computation

* to complete, and then retrieves its result.

*

* @param timeout Time to wait before cancelling the operation.

* @param unit The time unit for the timeout.

*

* @return The computed result.

*

* @throws CancellationException If the computation was cancelled.

* @throws ExecutionException If the computation threw an exception.

* @throws InterruptedException If the current thread was interrupted

* while waiting.

* @throws TimeoutException If the wait timed out.

*/

public final Result get(long timeout, TimeUnit unit) throws InterruptedException,

ExecutionException, TimeoutException {

return mFuture.get(timeout, unit);

}

/**

* Executes the task with the specified parameters. The task returns

* itself (this) so that the caller can keep a reference to it.

*

* <p>Note: this function schedules the task on a queue for a single background

* thread or pool of threads depending on the platform version. When first

* introduced, AsyncTasks were executed serially on a single background thread.

* Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed

* to a pool of threads allowing multiple tasks to operate in parallel. Starting

* {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being

* executed on a single thread to avoid common application errors caused

* by parallel execution. If you truly want parallel execution, you can use

* the {@link #executeOnExecutor} version of this method

* with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings

* on its use.

*

* <p>This method must be invoked on the UI thread.

*

* @param params The parameters of the task.

*

* @return This instance of AsyncTask.

*

* @throws IllegalStateException If {@link #getStatus()} returns either

* {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.

*

* @see #executeOnExecutor(java.util.concurrent.Executor, Object[])

* @see #execute(Runnable)

*/ /**

* 这个方法如何执行和系统版本有关,在AsyncTask的使用规则里已经说明,如果你真的想使用并行AsyncTask,

* 也是可以的,只要稍作修改

* 必须在UI线程调用此方法

*/

public final AsyncTask<Params, Progress, Result> execute(Params... params) {

//串行执行

return executeOnExecutor(sDefaultExecutor, params);

//如果我们想并行执行,这样改就行了,当然这个方法我们没法改

//return executeOnExecutor(THREAD_POOL_EXECUTOR, params);

}

/**

* Executes the task with the specified parameters. The task returns

* itself (this) so that the caller can keep a reference to it.

*

* <p>This method is typically used with {@link #THREAD_POOL_EXECUTOR} to

* allow multiple tasks to run in parallel on a pool of threads managed by

* AsyncTask, however you can also use your own {@link Executor} for custom

* behavior.

*

* <p><em>Warning:</em> Allowing multiple tasks to run in parallel from

* a thread pool is generally <em>not</em> what one wants, because the order

* of their operation is not defined. For example, if these tasks are used

* to modify any state in common (such as writing a file due to a button click),

* there are no guarantees on the order of the modifications.

* Without careful work it is possible in rare cases for the newer version

* of the data to be over-written by an older one, leading to obscure data

* loss and stability issues. Such changes are best

* executed in serial; to guarantee such work is serialized regardless of

* platform version you can use this function with {@link #SERIAL_EXECUTOR}.

*

* <p>This method must be invoked on the UI thread.

*

* @param exec The executor to use. {@link #THREAD_POOL_EXECUTOR} is available as a

* convenient process-wide thread pool for tasks that are loosely coupled.

* @param params The parameters of the task.

*

* @return This instance of AsyncTask.

*

* @throws IllegalStateException If {@link #getStatus()} returns either

* {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.

*

* @see #execute(Object[])

*/

/**

* 通过这个方法我们可以自定义AsyncTask的执行方式,串行or并行,甚至可以采用自己的Executor

* 为了实现并行,我们可以在外部这么用AsyncTask:

* asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, Params... params);

* 必须在UI线程调用此方法

*/

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,

Params... params) {

if (mStatus != Status.PENDING) {

switch (mStatus) {

case RUNNING:

throw new IllegalStateException("Cannot execute task:"

+ " the task is already running.");

case FINISHED:

throw new IllegalStateException("Cannot execute task:"

+ " the task has already been executed "

+ "(a task can be executed only once)");

}

}

mStatus = Status.RUNNING;

onPreExecute();

mWorker.mParams = params;

exec.execute(mFuture);

return this;

}

/**

* Convenience version of {@link #execute(Object...)} for use with

* a simple Runnable object. See {@link #execute(Object[])} for more

* information on the order of execution.

*

* @see #execute(Object[])

* @see #executeOnExecutor(java.util.concurrent.Executor, Object[])

*//**

* 这是AsyncTask提供的一个静态方法,方便我们直接执行一个runnable

*/

public static void execute(Runnable runnable) {

sDefaultExecutor.execute(runnable);

}

/**

* This method can be invoked from {@link #doInBackground} to

* publish updates on the UI thread while the background computation is

* still running. Each call to this method will trigger the execution of

* {@link #onProgressUpdate} on the UI thread.

*

* {@link #onProgressUpdate} will note be called if the task has been

* canceled.

*

* @param values The progress values to update the UI with.

*

* @see #onProgressUpdate

* @see #doInBackground

*/

protected final void publishProgress(Progress... values) {

if (!isCancelled()) {

sHandler.obtainMessage(MESSAGE_POST_PROGRESS,

new AsyncTaskResult<Progress>(this, values)).sendToTarget();

}

}

private void finish(Result result) {

if (isCancelled()) {

onCancelled(result);

} else {

onPostExecute(result);

}

mStatus = Status.FINISHED;

}

//AsyncTask内部Handler,用来发送后台计算进度更新消息和计算完成消息

private static class InternalHandler extends Handler {

@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})

@Override

public void handleMessage(Message msg) {

AsyncTaskResult result = (AsyncTaskResult) msg.obj;

switch (msg.what) {

case MESSAGE_POST_RESULT:

// There is only one result

result.mTask.finish(result.mData[0]);

break;

case MESSAGE_POST_PROGRESS:

result.mTask.onProgressUpdate(result.mData);

break;

}

}

}

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {

Params[] mParams;

}

@SuppressWarnings({"RawUseOfParameterizedType"})

private static class AsyncTaskResult<Data> {

final AsyncTask mTask;

final Data[] mData;

AsyncTaskResult(AsyncTask task, Data... data) {

mTask = task;

mData = data;

}

}

}

2、Thread+Handler模式

每个需要请求服务器的地方都创建一个线程,等到线程执行完毕就通过handler发送消息到主线程。这样的线程运行是不受控制的。

总结一下:

AsyncTask是封装好的线程池,比起Thread+Handler的方式,AsyncTask在操作UI线程上更方便,因为onPreExecute()、onPostExecute()及更新UI方法onProgressUpdate()均运行在主线程中,这样就不用Handler发消息处理了。

AsyncTask真正的缺点来自于3.0之前全局线程池只有5个工作线程,也就是说,一个APP如果运用AsyncTask技术来执行线程,那么同一时间最多只能有5个线程同时运行,其他线程将被阻塞,但是在3.0之后可以自定义执行器,多少个线程同时运行不受到限制。不运用AsyncTask执行的线程,也就是自己new出来的线程不受此限制。

所以在3.0之前AsyncTask不要用于多线程取网络数据,因为很可能这样会产生阻塞,从而降低效率。3.0之后我建议最好是使用AsyncTask。

5、为什么ScrollView中嵌套了ViewPager后,viewPager高度出现问题并且滑动失效?

ScrollView是可以滑动的,而viewPager也是可以进行滑动的,虽然说两者嵌套不违反view的嵌套原则,但是Android系统里面可以滑动的组件里面是不可以嵌套可滑动的组件的,因为这样组件不好测量。

下面讲述一下解决方案:

解决方法只需要在接近水平滚动时ScrollView不处理事件而交由其子View(即这里的ViewPager)处理即可,重写ScrollView的onInterceptTouchEvent函数

[java] view
plaincopy

<span style="font-family:Courier New;font-size:14px;">public class VerticalScrollView extends ScrollView {

private GestureDetector mGestureDetector;

public VerticalScrollView(Context context, AttributeSet attrs){

super(context, attrs);

mGestureDetector = new GestureDetector(context, new ScrollDetector());

}

@Override

public boolean onInterceptTouchEvent(MotionEvent ev) {

return super.onInterceptTouchEvent(ev) && mGestureDetector.onTouchEvent(ev);

}

class ScrollDetector extends SimpleOnGestureListener {

@Override

public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {

return (Math.abs(distanceY) > Math.abs(distanceX));

}

}

}</span>

对于高度的问题,可以使用下列解决方案:

1、设置ScrollView的fillViewPort为true

2、设置ViewPager的layout_height为一个固定高度,比如:100dip

6、在项目中美工一般给我们切几套图,才能够适配Android碎片化的终端?

这个问题是属于Android终端屏幕适配的问题。读者可以访问我下列的文章《Android屏幕适配》一文,便大概可以回答这个问题了。

我再此处只想讲述下我在项目中的适配是如何做的。在项目中我一般只会切一套图,这套图放在hdpi目录下面,这样有些地方可能还适配不了,那就需要切一些单独的图,但是整体来说工作量也只有一套图的工作量。

读者可能奇怪了,为什么你会切出来的娿图片放在hdpi目录下面,而不放在其他的mdpi、xdpi目录下面呢?我觉得有两点原因,

第一点:如果放在mdpi目录下,那么图片到了高分辨率的终端上面就会出现模糊的情况,用户体验太差,如果放在xdpi以上的目录,那么这些图片放在mdpi的目录下时候,有可能会出现内存崩溃的现象。

第二点:现在市场上屏幕最多的分辨率是480*800,所以对这个适配就好,其他的再修复适配。那么480*800和hdpi有什么关系呢?我们可以使用数学来算一下。480*800代表的屏幕是4.0英寸的,我们可以算一下这种情况下的终端密度是多少,((480^2 + 800^2)开根号 )/4英寸=233 ,233接近240。而240是hdpi所代表的屏幕密度。所以适配480*800的屏幕放在hdpi目录下最合适不过了。

那么480*800上的图该如何切呢?这就要看一下我上次的文章《Android屏幕适配》了 。在这里我也再讲述一下我的切图方案。如果一张20*20或者30*30的图标在480*800上面适配。那么在xdpi上面该切多大的呢?xdpi是与720*1280分辨率对应的,这两种分辨率是2:3的关系,480*800上的20*20可以适配,那么在720*1280切上30*30的图标就合适了。可是上面不是讲了我只切一套图吗,怎么这两种分辨下同一图标会有两个不同大小呢。别急啊,既然他们是2:3的关系,那么不可以去他们的公倍数吗2:3不就是6的倍数了,所以如果20*20的图标在480*800上适合,那么切一张60*60的图标就可以适配这两种分辨率的屏幕了。

以上是不可能解决所有问题的,具体问题具体对待!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: