Created
January 10, 2018 12:39
-
-
Save lightai/f3412ceb7d94b037e5418ec75a02ab6f to your computer and use it in GitHub Desktop.
AsyncLayoutInflater改进版本:解决Android support包下面的AsyncLayoutInflater不支持View创建Handler或者调用myLooper()方法的问题。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public final class AsyncLayoutInflater { | |
private static final String TAG = "AsyncLayoutInflater"; | |
LayoutInflater mInflater; | |
Handler mHandler; | |
InflateThread mInflateThread; | |
public AsyncLayoutInflater(@NonNull Context context) { | |
mInflater = new BasicInflater(context); | |
mHandler = new Handler(mHandlerCallback); | |
mInflateThread = InflateThread.getInstance(); | |
} | |
@UiThread | |
public void inflate(@LayoutRes int resid, @Nullable ViewGroup parent, | |
@NonNull OnInflateFinishedListener callback) { | |
if (callback == null) { | |
throw new NullPointerException("callback argument may not be null!"); | |
} | |
InflateRequest request = mInflateThread.obtainRequest(); | |
request.inflater = this; | |
request.resid = resid; | |
request.parent = parent; | |
request.callback = callback; | |
mInflateThread.enqueue(request); | |
} | |
private Handler.Callback mHandlerCallback = new Handler.Callback() { | |
@Override | |
public boolean handleMessage(Message msg) { | |
InflateRequest request = (InflateRequest) msg.obj; | |
if (request.view == null) { | |
request.view = mInflater.inflate( | |
request.resid, request.parent, false); | |
} | |
request.callback.onInflateFinished( | |
request.view, request.resid, request.parent); | |
mInflateThread.releaseRequest(request); | |
return true; | |
} | |
}; | |
public interface OnInflateFinishedListener { | |
void onInflateFinished(View view, int resid, ViewGroup parent); | |
} | |
private static class InflateRequest { | |
AsyncLayoutInflater inflater; | |
ViewGroup parent; | |
int resid; | |
View view; | |
OnInflateFinishedListener callback; | |
InflateRequest() { | |
} | |
} | |
private static class BasicInflater extends LayoutInflater { | |
private static final String[] sClassPrefixList = { | |
"android.widget.", | |
"android.webkit.", | |
"android.app." | |
}; | |
BasicInflater(Context context) { | |
super(context); | |
} | |
@Override | |
public LayoutInflater cloneInContext(Context newContext) { | |
return new BasicInflater(newContext); | |
} | |
@Override | |
protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException { | |
for (String prefix : sClassPrefixList) { | |
try { | |
View view = createView(name, prefix, attrs); | |
if (view != null) { | |
return view; | |
} | |
} catch (ClassNotFoundException e) { | |
// In this case we want to let the base class take a crack | |
// at it. | |
} | |
} | |
return super.onCreateView(name, attrs); | |
} | |
} | |
private static class InflateThread extends Thread { | |
private static final InflateThread sInstance; | |
static { | |
sInstance = new InflateThread(); | |
sInstance.start(); | |
} | |
public static InflateThread getInstance() { | |
return sInstance; | |
} | |
private ArrayBlockingQueue<InflateRequest> mQueue = new ArrayBlockingQueue<>(10); | |
private Pools.SynchronizedPool<InflateRequest> mRequestPool = new Pools.SynchronizedPool<>(10); | |
// Extracted to its own method to ensure locals have a constrained liveness | |
// scope by the GC. This is needed to avoid keeping previous request references | |
// alive for an indeterminate amount of time, see b/33158143 for details | |
public void runInner() { | |
InflateRequest request; | |
try { | |
request = mQueue.take(); | |
} catch (InterruptedException ex) { | |
// Odd, just continue | |
Log.w(TAG, ex); | |
return; | |
} | |
try { | |
request.view = request.inflater.mInflater.inflate( | |
request.resid, request.parent, false); | |
} catch (RuntimeException ex) { | |
// Probably a Looper failure, retry on the UI thread | |
Log.w(TAG, "Failed to inflate resource in the background! Retrying on the UI" | |
+ " thread", ex); | |
} | |
Message.obtain(request.inflater.mHandler, 0, request) | |
.sendToTarget(); | |
} | |
@Override | |
public void run() { | |
ThreadLocal<Looper> sThreadLocal = null; | |
try { | |
Field field = Looper.class.getDeclaredField("sThreadLocal"); | |
field.setAccessible(true); | |
Object object = field.get(Looper.getMainLooper()); | |
if (object instanceof ThreadLocal) { | |
sThreadLocal = (ThreadLocal<Looper>) object; | |
sThreadLocal.set(Looper.getMainLooper()); | |
} | |
Log.i("AsyncLayoutInflater", "myLooper:" + Looper.myLooper()); | |
} catch (NoSuchFieldException e) { | |
e.printStackTrace(); | |
} catch (IllegalAccessException e) { | |
e.printStackTrace(); | |
} | |
while (true) { | |
runInner(); | |
} | |
} | |
public InflateRequest obtainRequest() { | |
InflateRequest obj = mRequestPool.acquire(); | |
if (obj == null) { | |
obj = new InflateRequest(); | |
} | |
return obj; | |
} | |
public void releaseRequest(InflateRequest obj) { | |
obj.callback = null; | |
obj.inflater = null; | |
obj.parent = null; | |
obj.resid = 0; | |
obj.view = null; | |
mRequestPool.release(obj); | |
} | |
public void enqueue(InflateRequest request) { | |
try { | |
mQueue.put(request); | |
} catch (InterruptedException e) { | |
throw new RuntimeException( | |
"Failed to enqueue async inflate request", e); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment