android.os.Looper

Here are the examples of the java api android.os.Looper taken from open source projects. By voting up you can indicate which examples are most useful and appropriate.

311 Examples 7

19 Source : IjkMediaPlayer.java
with MIT License
from zzh12138

private void initPlayer(IjkLibLoader libLoader, Context context) {
    loadLibrariesOnce(libLoader, context);
    initNativeOnce();
    Looper looper;
    if ((looper = Looper.myLooper()) != null) {
        mEventHandler = new EventHandler(this, looper);
    } else if ((looper = Looper.getMainLooper()) != null) {
        mEventHandler = new EventHandler(this, looper);
    } else {
        mEventHandler = null;
    }
    /*
         * Native setup requires a weak reference to our object. It's easier to
         * create it here than in C++.
         */
    native_setup(new WeakReference<IjkMediaPlayer>(this));
}

19 Source : VideoPlayer.java
with MIT License
from Zhaoss

private void initPlayer() {
    initNativeOnce();
    Looper looper;
    if ((looper = Looper.myLooper()) != null) {
        mEventHandler = new EventHandler(this, looper);
    } else if ((looper = Looper.getMainLooper()) != null) {
        mEventHandler = new EventHandler(this, looper);
    } else {
        mEventHandler = null;
    }
    native_setup(new WeakReference<VideoPlayer>(this));
}

19 Source : LoaderService.java
with MIT License
from zerobranch

public clreplaced LoaderService extends Service {

    private volatile Looper serviceLooper;

    private volatile Handler handler;

    public static final int DEFAULT_NOTIFICATION_ID = 43534;

    private ArrayList<String> urls;

    @Override
    public void onCreate() {
        super.onCreate();
        final HandlerThread thread = new HandlerThread(LoaderService.clreplaced.getName());
        thread.start();
        serviceLooper = thread.getLooper();
        handler = new Handler(serviceLooper, handlerCallback);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        urls = intent.getStringArrayListExtra(BundleConst.URL);
        for (int i = 0; i < urls.size(); i++) {
            final Message msg = handler.obtainMessage();
            msg.arg1 = startId;
            msg.arg2 = i;
            msg.obj = intent;
            handler.sendMessage(msg);
        }
        return START_NOT_STICKY;
    }

    @SuppressWarnings("deprecation")
    private void setImmortal(Intent intent) {
        final boolean isHideNotification = intent.getBooleanExtra(BundleConst.DEFAULT_NOTIFICATION, false);
        final boolean isImmortal = intent.getBooleanExtra(BundleConst.IMMORTAL, false);
        final Notification notification = intent.getParcelableExtra(BundleConst.NOTIFICATION);
        if (isImmortal) {
            if (notification != null) {
                startForeground(DEFAULT_NOTIFICATION_ID, notification);
            } else {
                startForeground(DEFAULT_NOTIFICATION_ID, new Notification.Builder(this).build());
                if (isHideNotification) {
                    startService(new Intent(this, HideNotificationService.clreplaced));
                }
            }
        }
    }

    @Override
    public void onDestroy() {
        serviceLooper.quit();
        serviceLooper.getThread().interrupt();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private final Handler.Callback handlerCallback = new Handler.Callback() {

        @Override
        public boolean handleMessage(Message msg) {
            final Intent intent = (Intent) msg.obj;
            final int urlIndex = msg.arg2;
            setImmortal(intent);
            onHandleIntent(intent, urls.get(urlIndex));
            stopForeground(intent.getBooleanExtra(BundleConst.VIEW_NOTIFICATION_ON_FINISH, false));
            if (urlIndex == urls.size() - 1) {
                stopSelf(msg.arg1);
            }
            return true;
        }
    };

    private void onHandleIntent(Intent intent, String url) {
        final String path = intent.getStringExtra(BundleConst.PATH);
        final ResultReceiver receiver = intent.getParcelableExtra(BundleConst.RECEIVER);
        new LoadManager().skipIfFileExist(intent.getBooleanExtra(BundleConst.SKIP_IF_EXIST, false)).abortNextIfError(intent.getBooleanExtra(BundleConst.ABORT_IF_ERROR, false)).redownloadAttemptCount(intent.getIntExtra(BundleConst.REDOWNLOAD_COUNT, 0)).loadFile(path, url, receiver);
    }
}

19 Source : Daemon.java
with Apache License 2.0
from YuntaoWei

/**
 * 常驻的后台线程,用于处理一个消息循环
 * <p/>
 * 一般用于处理运算密集型任务、磁盘IO任务等执行时间小于1秒的任务
 */
public clreplaced Daemon {

    private static volatile boolean shouldStop;

    private static Thread thread = null;

    private static Looper looper = null;

    public static synchronized void start() {
        if (thread == null) {
            final BlockingItem<Looper> bl = new BlockingItem<Looper>();
            thread = new Thread(new Runnable() {

                @Override
                public void run() {
                    Looper.prepare();
                    Looper l = Looper.myLooper();
                    bl.put(l);
                    while (!shouldStop) {
                        try {
                            Looper.loop();
                        } catch (Exception e) {
                        }
                    }
                }
            }, "daemon");
            shouldStop = false;
            thread.start();
            try {
                looper = bl.take();
            } catch (InterruptedException e) {
            }
        }
    }

    public static synchronized void stop() {
        shouldStop = true;
        if (thread != null && looper != null) {
            looper.quit();
            try {
                thread.join();
            } catch (Exception e) {
            }
            thread = null;
            looper = null;
        }
    }

    public static Looper looper() {
        if (looper == null) {
            start();
        }
        return looper == null ? Looper.getMainLooper() : looper;
    }
}

19 Source : PermissionsResultAction.java
with Apache License 2.0
from ykbjson

/**
 * Desription:<p>This abstract clreplaced should be used to create an if/else action that the PermissionsManager
 * can execute when the permissions you request are granted or denied. Simple use involves
 * creating an anonymous instance of it and preplaceding that instance to the
 * requestPermissionsIfNecessaryForResult method. The result will be sent back to you as
 * either onGranted (all permissions have been granted), or onDenied (a required permission
 * has been denied). Ideally you put your functionality in the onGranted method and notify
 * the user what won't work in the onDenied method.</p>
 * Creator:yankebin
 * CreatedAt:2018/11/7
 */
public clreplaced PermissionsResultAction {

    private final String TAG = getClreplaced().getName();

    private Looper mLooper = Looper.getMainLooper();

    private final Set<String> mPermissions = new HashSet<>(1);

    private final Set<String> mDeniedPermissions = new HashSet<>(1);

    private final PermissionsRequestCallback mPermissionsRequestCallback;

    private final int mRequestCode;

    /**
     * Default Constructor
     */
    public PermissionsResultAction(int requestCode, @Nullable PermissionsRequestCallback permissionsRequestCallback) {
        mRequestCode = requestCode;
        mPermissionsRequestCallback = permissionsRequestCallback;
    }

    /**
     * Alternate Constructor. Preplaced the looper you wish the PermissionsResultAction
     * callbacks to be executed on if it is not the current Looper. For instance,
     * if you are making a permissions request from a background thread but wish the
     * callback to be on the UI thread, use this constructor to specify the UI Looper.
     *
     * @param looper the looper that the callbacks will be called using.
     */
    public PermissionsResultAction(int requestCode, @Nullable PermissionsRequestCallback permissionsRequestCallback, @NonNull Looper looper) {
        this(requestCode, permissionsRequestCallback);
        mLooper = looper;
    }

    /**
     * This method is called when a permission that have been
     * requested have been granted by the user. In this method
     * you should put your permission(s) sensitive code that can
     * only be executed with the required permissions.
     */
    public void onGranted(String permission) {
        if (null != mPermissionsRequestCallback) {
            mPermissionsRequestCallback.onGranted(mRequestCode, permission);
        }
    }

    /**
     * This method is called when a permission has been denied by
     * the user. It provides you with the permission that was denied
     * and will be executed on the Looper you preplaced to the constructor
     * of this clreplaced, or the Looper that this object was created on.
     *
     * @param permission the permission that was denied.
     */
    public void onDenied(String permission) {
        if (null != mPermissionsRequestCallback) {
            mPermissionsRequestCallback.onDenied(mRequestCode, permission);
        }
    }

    /**
     * This method is called when a permission has been denied by
     * the user forever. It provides you with the permission that was denied
     * and will be executed on the Looper you preplaced to the constructor
     * of this clreplaced, or the Looper that this object was created on.
     *
     * @param permission the permission that was denied.
     */
    public void onDeniedForever(String permission) {
        if (null != mPermissionsRequestCallback) {
            mPermissionsRequestCallback.onDeniedForever(mRequestCode, permission);
        }
    }

    /**
     * This method is called when all permissions has been check complete
     * but some permissions denied.
     *
     * @param deniedPermissions those denied permissions
     */
    public void onFailure(String[] deniedPermissions) {
        if (null != mPermissionsRequestCallback) {
            mPermissionsRequestCallback.onFailure(mRequestCode, deniedPermissions);
        }
    }

    /**
     * This method is called when all permissions has been check complete
     * and all permissions granted.
     */
    public void onSuccess() {
        if (null != mPermissionsRequestCallback) {
            mPermissionsRequestCallback.onSuccess(mRequestCode);
        }
    }

    /**
     * This method is used to determine if a permission not
     * being present on the current Android platform should
     * affect whether the PermissionsResultAction should continue
     * listening for events. By default, it returns true and will
     * simply ignore the permission that did not exist. Usually this will
     * work fine since most new permissions are introduced to
     * restrict what was previously allowed without permission.
     * If that is not the case for your particular permission you
     * request, override this method and return false to result in the
     * Action being denied.
     *
     * @param permission the permission that doesn't exist on this
     *                   Android version
     * @return return true if the PermissionsResultAction should
     * ignore the lack of the permission and proceed with exection
     * or false if the PermissionsResultAction should treat the
     * absence of the permission on the API level as a denial.
     */
    public synchronized boolean shouldIgnorePermissionNotFound(String permission) {
        if (PermissionsManager.getInstance().isEnableLog()) {
            Log.d(TAG, "Permission not found: " + permission);
        }
        return true;
    }

    @CallSuper
    protected synchronized final boolean onResult(@NonNull final String permission, int result) {
        if (result == PackageManager.PERMISSION_GRANTED) {
            return onResult(permission, Permissions.GRANTED);
        } else {
            return onResult(permission, Permissions.DENIED);
        }
    }

    /**
     * This method is called when a particular permission has changed.
     * This method will be called for all permissions, so this method determines
     * if the permission affects the state or not and whether it can proceed with
     * calling onGranted or if onDenied should be called.
     *
     * @param permission the permission that changed.
     * @param result     the result for that permission.
     * @return this method returns true if its primary action has been completed
     * and it should be removed from the data structure holding a reference to it.
     */
    @CallSuper
    protected synchronized final boolean onResult(@NonNull final String permission, Permissions result) {
        mPermissions.remove(permission);
        boolean onResult = false;
        if (result == Permissions.GRANTED) {
            getSchedule().scheduleDirect(new Runnable() {

                @Override
                public void run() {
                    PermissionsResultAction.this.onGranted(permission);
                }
            });
        } else if (result == Permissions.DENIED) {
            getSchedule().scheduleDirect(new Runnable() {

                @Override
                public void run() {
                    PermissionsResultAction.this.onDenied(permission);
                }
            });
        } else if (result == Permissions.NOT_FOUND) {
            if (shouldIgnorePermissionNotFound(permission)) {
                getSchedule().scheduleDirect(new Runnable() {

                    @Override
                    public void run() {
                        PermissionsResultAction.this.onGranted(permission);
                    }
                });
            } else {
                getSchedule().scheduleDirect(new Runnable() {

                    @Override
                    public void run() {
                        PermissionsResultAction.this.onDenied(permission);
                    }
                });
            }
        } else if (result == Permissions.USER_DENIED_FOREVER) {
            getSchedule().scheduleDirect(new Runnable() {

                @Override
                public void run() {
                    PermissionsResultAction.this.onDeniedForever(permission);
                }
            });
        }
        // 标记不被通过的权限
        if (result == Permissions.DENIED || (result == Permissions.NOT_FOUND && !shouldIgnorePermissionNotFound(permission))) {
            mDeniedPermissions.add(permission);
        }
        if (mPermissions.isEmpty()) {
            getSchedule().scheduleDirect(new Runnable() {

                @Override
                public void run() {
                    if (mDeniedPermissions.isEmpty()) {
                        PermissionsResultAction.this.onSuccess();
                    } else {
                        final String[] deniedPermissions = (String[]) mDeniedPermissions.toArray(new String[mDeniedPermissions.size()]);
                        PermissionsResultAction.this.onFailure(deniedPermissions);
                    }
                    // 重置不被通过的权限存储
                    mDeniedPermissions.clear();
                }
            });
            onResult = true;
        }
        return onResult;
    }

    /**
     * This method registers the PermissionsResultAction object for the specified permissions
     * so that it will know which permissions to look for changes to. The PermissionsResultAction
     * will then know to look out for changes to these permissions.
     *
     * @param perms the permissions to listen for
     */
    @SuppressWarnings("WeakerAccess")
    @CallSuper
    protected synchronized final void registerPermissions(@NonNull String[] perms) {
        Collections.addAll(mPermissions, perms);
    }

    /**
     * 获取执行权限回调的线程调度器
     *
     * @return
     */
    protected synchronized Scheduler getSchedule() {
        return null == mLooper ? AndroidSchedulers.mainThread() : AndroidSchedulers.from(mLooper);
    }

    protected int getRequestCode() {
        return mRequestCode;
    }
}

19 Source : Player.java
with MIT License
from xinpianchang

/**
 * 使用播放器模块  需要主动调用该类init方法
 */
public clreplaced Player {

    private static Looper mStateMachineLooper;

    public static void init(@NonNull Context context) {
        init(context, null);
    }

    public static void init(@NonNull Context context, @Nullable Looper looper) {
        ConnectionUtils.init(context);
        // 初始化播放器所需要的Looper.
        if (looper == null) {
            HandlerThread handlerThread = new HandlerThread("player");
            handlerThread.start();
            mStateMachineLooper = handlerThread.getLooper();
        } else {
            mStateMachineLooper = looper;
        }
    }

    @NonNull
    public static Looper getStateMachineLooper() {
        if (mStateMachineLooper == null) {
            throw new IllegalArgumentException("You must init first.");
        }
        return mStateMachineLooper;
    }
}

19 Source : IjkMediaPlayer.java
with GNU General Public License v3.0
from xdtianyu

private void initPlayer(IjkLibLoader libLoader) {
    loadLibrariesOnce(libLoader);
    initNativeOnce();
    Looper looper;
    if ((looper = Looper.myLooper()) != null) {
        mEventHandler = new EventHandler(this, looper);
    } else if ((looper = Looper.getMainLooper()) != null) {
        mEventHandler = new EventHandler(this, looper);
    } else {
        mEventHandler = null;
    }
    /*
         * Native setup requires a weak reference to our object. It's easier to
         * create it here than in C++.
         */
    native_setup(new WeakReference<IjkMediaPlayer>(this));
}

19 Source : FimiMediaPlayer.java
with MIT License
from wladimir-computin

private void initPlayer(FimiLibLoader libLoader) {
    loadLibrariesOnce(libLoader);
    initNativeOnce();
    Looper looper = Looper.myLooper();
    if (looper != null) {
        this.mEventHandler = new EventHandler(this, looper);
    } else {
        looper = Looper.getMainLooper();
        if (looper != null) {
            this.mEventHandler = new EventHandler(this, looper);
        } else {
            this.mEventHandler = null;
        }
    }
    native_setup(new WeakReference(this));
}

19 Source : SystemClock.java
with GNU General Public License v2.0
from warren-bank

@Override
public HandlerWrapper createHandler(Looper looper, @Nullable Callback callback) {
    return new SystemHandlerWrapper(new Handler(looper, callback));
}

19 Source : Loader.java
with GNU General Public License v2.0
from warren-bank

/**
 * Starts loading a {@link Loadable}.
 *
 * <p>The calling thread must be a {@link Looper} thread, which is the thread on which the {@link
 * Callback} will be called.
 *
 * @param <T> The type of the loadable.
 * @param loadable The {@link Loadable} to load.
 * @param callback A callback to be called when the load ends.
 * @param looper The {@link Looper} thread.
 * @param defaultMinRetryCount The minimum number of times the load must be retried before {@link
 *     #maybeThrowError()} will propagate an error.
 * @throws IllegalStateException If the calling thread does not have an replacedociated {@link Looper}.
 * @return {@link SystemClock#elapsedRealtime} when the load started.
 */
public <T extends Loadable> long startLoading(T loadable, Callback<T> callback, Looper looper, int defaultMinRetryCount) {
    replacedertions.checkState(looper != null);
    fatalError = null;
    long startTimeMs = SystemClock.elapsedRealtime();
    new LoadTask<>(looper, loadable, callback, defaultMinRetryCount, startTimeMs).start(0);
    return startTimeMs;
}

19 Source : DefaultDrmSessionManager.java
with GNU General Public License v2.0
from warren-bank

@Override
public DrmSession<T> acquireSession(Looper playbackLooper, DrmInitData drmInitData) {
    replacedertions.checkState(this.playbackLooper == null || this.playbackLooper == playbackLooper);
    if (sessions.isEmpty()) {
        this.playbackLooper = playbackLooper;
        if (mediaDrmHandler == null) {
            mediaDrmHandler = new MediaDrmHandler(playbackLooper);
        }
    }
    List<SchemeData> schemeDatas = null;
    if (offlineLicenseKeySetId == null) {
        schemeDatas = getSchemeDatas(drmInitData, uuid, false);
        if (schemeDatas.isEmpty()) {
            final MissingSchemeDataException error = new MissingSchemeDataException(uuid);
            eventDispatcher.dispatch(listener -> listener.onDrmSessionManagerError(error));
            return new ErrorStateDrmSession<>(new DrmSessionException(error));
        }
    }
    DefaultDrmSession<T> session;
    if (!multiSession) {
        session = sessions.isEmpty() ? null : sessions.get(0);
    } else {
        // Only use an existing session if it has matching init data.
        session = null;
        for (DefaultDrmSession<T> existingSession : sessions) {
            if (Util.areEqual(existingSession.schemeDatas, schemeDatas)) {
                session = existingSession;
                break;
            }
        }
    }
    if (session == null) {
        // Create a new session.
        session = new DefaultDrmSession<>(uuid, mediaDrm, this, schemeDatas, mode, offlineLicenseKeySetId, optionalKeyRequestParameters, callback, playbackLooper, eventDispatcher, initialDrmRequestRetryCount);
        sessions.add(session);
    }
    session.acquire();
    return session;
}

19 Source : HTTPTask.java
with Apache License 2.0
from vimfung

/**
 * 上传文件
 *
 * @param fileParams      文件参数
 * @param parameters      请求参数
 * @param resultHandler   返回回调
 * @param faultHandler    失败回调
 * @param progressHandler 上传进度回调
 */
public void upload(final HashMap<String, HTTPFile> fileParams, final HashMap<String, String> parameters, final LuaFunction resultHandler, final LuaFunction faultHandler, final LuaFunction progressHandler) {
    if (url == null) {
        return;
    }
    // 取消之前的请求
    cancel();
    Looper mainLooper = Looper.getMainLooper();
    final Handler mainHandler = new Handler(mainLooper);
    new Thread(new Runnable() {

        @Override
        public void run() {
            try {
                // 总上传字节
                long totalUploadedBytes = 0;
                long uploadedBytes = 0;
                URL requestURL = new URL(url);
                _curConn = (HttpURLConnection) requestURL.openConnection();
                try {
                    _curConn.setRequestMethod("POST");
                    _curConn.setConnectTimeout(timeout * 1000);
                    // 分块传输
                    _curConn.setChunkedStreamingMode(4096);
                    fillHttpHeaders();
                    // 生成Boundary String
                    int boundaryStringId = new Random().nextInt(9999999 - 123400) + 123400;
                    String boundaryString = String.format(Locale.getDefault(), "Boundary-%d", boundaryStringId);
                    String contentType = String.format(Locale.getDefault(), "multipart/form-data; boundary=%s", boundaryString);
                    _curConn.addRequestProperty("Content-Type", contentType);
                    _curConn.setDoOutput(true);
                    _curConn.setDoInput(true);
                    // POST请求不能用缓存,设置为false
                    _curConn.setUseCaches(false);
                    // 连接服务器
                    _curConn.connect();
                    // 得到httpURLConnection的输出流
                    OutputStream os = _curConn.getOutputStream();
                    try {
                        os.write(String.format(Locale.getDefault(), "--%s\r\n", boundaryString).getBytes());
                        String endItemBoundaryString = String.format(Locale.getDefault(), "\r\n--%s\r\n", boundaryString);
                        // 填充上传文件数据
                        if (fileParams != null) {
                            // 计算总的文件大小
                            totalUploadedBytes = getUploadFilesLength(fileParams);
                            // 写入文件数据
                            sendUploadFilesData(fileParams, os, boundaryString, totalUploadedBytes, progressHandler);
                        }
                        // 写入请求参数
                        if (parameters != null) {
                            int paramIndex = 0;
                            for (Map.Entry<String, String> entry : parameters.entrySet()) {
                                String contentDisposition = String.format(Locale.getDefault(), "Content-Disposition: form-data; name=\"%s\"\r\n\r\n", Encoding.urlEncode(entry.getKey()));
                                os.write(contentDisposition.getBytes());
                                os.write(Encoding.urlEncode(entry.getValue()).getBytes());
                                if (paramIndex < parameters.size() - 1) {
                                    os.write(endItemBoundaryString.getBytes());
                                }
                                paramIndex++;
                            }
                        }
                        os.write(String.format(Locale.getDefault(), "\r\n--%s--\r\n", boundaryString).getBytes());
                        // 刷新对象输出流,将字节全部写入输出流中
                        os.flush();
                    } finally {
                        // 关闭流对象
                        os.close();
                    }
                    dealResponse(null, resultHandler, faultHandler, null);
                } finally {
                    _curConn.disconnect();
                }
            } catch (final MalformedURLException e) {
                mainHandler.post(new Runnable() {

                    @Override
                    public void run() {
                        if (faultHandler != null) {
                            faultHandler.invoke(new LuaValue[] { new LuaValue(e.getMessage()) });
                        }
                    }
                });
            } catch (final IOException e) {
                mainHandler.post(new Runnable() {

                    @Override
                    public void run() {
                        if (faultHandler != null) {
                            faultHandler.invoke(new LuaValue[] { new LuaValue(e.getMessage()) });
                        }
                    }
                });
            }
        }
    }).start();
}

19 Source : HTTPTask.java
with Apache License 2.0
from vimfung

/**
 * 发起POST请求
 *
 * @param parameters    请求参数
 * @param resultHandler 返回回调
 * @param faultHandler  失败回调
 */
public void post(final Map<String, String> parameters, final LuaFunction resultHandler, final LuaFunction faultHandler) {
    if (url == null) {
        return;
    }
    // 取消之前的请求
    cancel();
    Looper mainLooper = Looper.getMainLooper();
    final Handler mainHandler = new Handler(mainLooper);
    new Thread(new Runnable() {

        @Override
        public void run() {
            try {
                URL requestURL = new URL(url);
                _curConn = (HttpURLConnection) requestURL.openConnection();
                try {
                    _curConn.setRequestMethod("POST");
                    _curConn.setConnectTimeout(timeout * 1000);
                    fillHttpHeaders();
                    _curConn.setDoOutput(true);
                    _curConn.setDoInput(true);
                    // POST请求不能用缓存,设置为false
                    _curConn.setUseCaches(false);
                    // 连接服务器
                    _curConn.connect();
                    // 得到httpURLConnection的输出流
                    OutputStream os = _curConn.getOutputStream();
                    try {
                        // 向对象输出流写出数据,这些数据将存到内存缓冲区中
                        os.write(getParametersString(parameters).getBytes());
                        // 刷新对象输出流,将字节全部写入输出流中
                        os.flush();
                    } finally {
                        // 关闭流对象
                        os.close();
                    }
                    dealResponse(null, resultHandler, faultHandler, null);
                } finally {
                    _curConn.disconnect();
                }
            } catch (final MalformedURLException e) {
                mainHandler.post(new Runnable() {

                    @Override
                    public void run() {
                        if (faultHandler != null) {
                            faultHandler.invoke(new LuaValue[] { new LuaValue(e.getMessage()) });
                        }
                    }
                });
            } catch (final IOException e) {
                mainHandler.post(new Runnable() {

                    @Override
                    public void run() {
                        if (faultHandler != null) {
                            faultHandler.invoke(new LuaValue[] { new LuaValue(e.getMessage()) });
                        }
                    }
                });
            }
        }
    }).start();
}

19 Source : HTTPTask.java
with Apache License 2.0
from vimfung

/**
 * 下载文件
 *
 * @param filePath 保存文件的路径
 * @param resultHandler   返回回调
 * @param faultHandler    失败回调
 * @param progressHandler 下载进度回调
 */
public void download(final String filePath, final LuaFunction resultHandler, final LuaFunction faultHandler, final LuaFunction progressHandler) {
    if (url == null) {
        return;
    }
    // 取消之前的请求
    cancel();
    Looper mainLooper = Looper.getMainLooper();
    final Handler mainHandler = new Handler(mainLooper);
    new Thread(new Runnable() {

        @Override
        public void run() {
            try {
                URL requestURL = new URL(url);
                _curConn = (HttpURLConnection) requestURL.openConnection();
                try {
                    _curConn.setRequestMethod("GET");
                    _curConn.setConnectTimeout(timeout * 1000);
                    fillHttpHeaders();
                    _curConn.connect();
                    dealResponse(filePath, resultHandler, faultHandler, progressHandler);
                } finally {
                    _curConn.disconnect();
                }
            } catch (final MalformedURLException e) {
                mainHandler.post(new Runnable() {

                    @Override
                    public void run() {
                        if (faultHandler != null) {
                            faultHandler.invoke(new LuaValue[] { new LuaValue(e.getMessage()) });
                        }
                    }
                });
            } catch (final IOException e) {
                mainHandler.post(new Runnable() {

                    @Override
                    public void run() {
                        if (faultHandler != null) {
                            faultHandler.invoke(new LuaValue[] { new LuaValue(e.getMessage()) });
                        }
                    }
                });
            }
        }
    }).start();
}

19 Source : HTTPTask.java
with Apache License 2.0
from vimfung

/**
 * 发起GET请求
 *
 * @param resultHandler 返回回调
 * @param faultHandler  失败回调
 */
public void get(final LuaFunction resultHandler, final LuaFunction faultHandler) {
    if (url == null) {
        return;
    }
    // 先取消之前请求
    cancel();
    Looper mainLooper = Looper.getMainLooper();
    final Handler mainHandler = new Handler(mainLooper);
    new Thread(new Runnable() {

        @Override
        public void run() {
            try {
                URL requestURL = new URL(url);
                _curConn = (HttpURLConnection) requestURL.openConnection();
                try {
                    _curConn.setRequestMethod("GET");
                    _curConn.setConnectTimeout(timeout * 1000);
                    fillHttpHeaders();
                    _curConn.connect();
                    dealResponse(null, resultHandler, faultHandler, null);
                } finally {
                    _curConn.disconnect();
                }
            } catch (final MalformedURLException e) {
                mainHandler.post(new Runnable() {

                    @Override
                    public void run() {
                        if (faultHandler != null) {
                            faultHandler.invoke(new LuaValue[] { new LuaValue(e.getMessage()) });
                        }
                    }
                });
            } catch (final IOException e) {
                mainHandler.post(new Runnable() {

                    @Override
                    public void run() {
                        if (faultHandler != null) {
                            faultHandler.invoke(new LuaValue[] { new LuaValue(e.getMessage()) });
                        }
                    }
                });
            }
        }
    }).start();
}

19 Source : LooperActorThread.java
with Apache License 2.0
from truecaller

/* package */
clreplaced LooperActorThread implements ActorThread {

    @NonNull
    private final FailureHandler mFailureHandler;

    @NonNull
    private final ProxyFactory mProxyFactory;

    @NonNull
    private final Looper mLooper;

    /* package */
    LooperActorThread(@NonNull ProxyFactory proxyFactory, @NonNull FailureHandler failureHandler, @NonNull Looper looper) {
        mProxyFactory = proxyFactory;
        mFailureHandler = failureHandler;
        mLooper = looper;
    }

    @NonNull
    @Override
    public <T> ActorRef<T> bind(@NonNull Clreplaced<T> cls, @NonNull T impl) {
        MessageSender postman = new LooperMessageSender<>(mLooper, mFailureHandler, impl);
        T instance = mProxyFactory.newProxy(cls, postman);
        return new ActorRefImpl<>(instance);
    }

    private static clreplaced LooperMessageSender<T> extends Handler implements MessageSender {

        @NonNull
        private final FailureHandler mFailureHandler;

        @NonNull
        private final T mImpl;

        /* package */
        LooperMessageSender(@NonNull Looper looper, @NonNull FailureHandler failureHandler, @NonNull T impl) {
            super(looper);
            mFailureHandler = failureHandler;
            mImpl = impl;
        }

        @Override
        public void deliver(@NonNull Message message) {
            android.os.Message msg = obtainMessage(0, message);
            msg.sendToTarget();
        }

        @SuppressWarnings("unchecked")
        @Override
        public void handleMessage(@NonNull android.os.Message msg) {
            Message<T, ?> message = (Message<T, ?>) msg.obj;
            try {
                message.invoke(mImpl);
            } catch (Throwable e) {
                ActorInvokeException call = message.exception();
                call.initCause(e);
                mFailureHandler.onUncaughtException(mImpl, message, call);
            }
        }
    }
}

19 Source : DefaultActorThread.java
with Apache License 2.0
from truecaller

private void stopThread(@NonNull Looper looper) {
    ActorHandlerBase handler = mHandler;
    synchronized (this) {
        if (handler == mHandler) {
            mHandler = null;
        }
    }
    looper.quit();
}

19 Source : ActorsThreadsBase.java
with Apache License 2.0
from truecaller

@Override
@NonNull
public ActorThread createThread(@NonNull Looper looper) {
    return new LooperActorThread(mProxyFactory, mFailureHandler, looper);
}

19 Source : TDQuitSafelyService.java
with Apache License 2.0
from ThinkingDataAnalytics

private void quitSafely(String threadName, long timeout) {
    try {
        for (Thread t : Thread.getAllStackTraces().keySet()) {
            if (t.getName().equals(threadName)) {
                try {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
                        if (t instanceof HandlerThread) {
                            Looper l = ((HandlerThread) t).getLooper();
                            if (null != l) {
                                l.quitSafely();
                                if (timeout > 0) {
                                    t.join(timeout);
                                } else {
                                    t.join(500);
                                }
                            }
                        }
                    } else {
                        // Just wait for sending exception data
                        Thread.sleep(500);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

19 Source : LooperWorkRunner.java
with Apache License 2.0
from spotify

/**
 * Creates a {@link WorkRunner} backed by the provided {@link Looper}
 *
 * @param looper the looper to use for processing work
 * @return a {@link WorkRunner} that uses the provided {@link Looper} for processing work
 */
public static LooperWorkRunner using(Looper looper) {
    return new LooperWorkRunner(looper);
}

19 Source : LooperCompatUtils.java
with Apache License 2.0
from sergchil

public static void quitSafely(final Looper looper) {
    if (null != METHOD_quitSafely) {
        CompatUtils.invoke(looper, null, /* default return value */
        METHOD_quitSafely);
    } else {
        looper.quit();
    }
}

19 Source : ByteBufferReader.java
with MIT License
from samigehi

private static PriorityQueue<ByteBuffer> getReclaimed() {
    Looper mainLooper = Looper.getMainLooper();
    if (mainLooper != null) {
        if (Thread.currentThread() == mainLooper.getThread())
            return null;
    }
    return reclaimed;
}

19 Source : HandlerThreadHandler.java
with Apache License 2.0
from saki4510t

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public void quitSafely() throws IllegalStateException {
    final Looper looper = getLooper();
    if (looper != null) {
        looper.quitSafely();
    } else {
        throw new IllegalStateException("has no looper");
    }
}

19 Source : HandlerThreadHandler.java
with Apache License 2.0
from saki4510t

public boolean isCurrentThread() throws IllegalStateException {
    final Looper looper = getLooper();
    if (looper != null) {
        return mId == Thread.currentThread().getId();
    } else {
        throw new IllegalStateException("has no looper");
    }
}

19 Source : HandlerThreadHandler.java
with Apache License 2.0
from saki4510t

public void quit() throws IllegalStateException {
    final Looper looper = getLooper();
    if (looper != null) {
        looper.quit();
    } else {
        throw new IllegalStateException("has no looper");
    }
}

19 Source : ThreadUtils.java
with BSD 3-Clause "New" or "Revised" License
from ridi

public static void setUiThread(Looper looper) {
    synchronized (sLock) {
        if (looper == null) {
            // Used to reset the looper after tests.
            sUiThreadHandler = null;
            return;
        }
        if (sUiThreadHandler != null && sUiThreadHandler.getLooper() != looper) {
            throw new RuntimeException("UI thread looper is already set to " + sUiThreadHandler.getLooper() + " (Main thread looper is " + Looper.getMainLooper() + "), cannot set to new looper " + looper);
        } else {
            sUiThreadHandler = new Handler(looper);
        }
    }
    TraceEvent.onUiThreadReady();
}

19 Source : Main.java
with Apache License 2.0
from rayworks

/**
 * Created by seanzhou on 3/14/17.
 */
public clreplaced Main {

    private static final String sTAG = Main.clreplaced.getName();

    private static final String IMAGE_JPEG = "image/jpeg";

    private static final String IMAGE_WEBP = "image/webp";

    private static final String IMAGE_PNG = "image/png";

    private static final String WIDTH = "width";

    private static final String HEIGHT = "height";

    private static final String FORMAT = "format";

    private static final int SCREENSHOT_DELAY_MILLIS = 1500;

    private static Looper looper;

    private static int width = 0;

    private static int height = 0;

    private static int port = 53516;

    private static DisplayUtil displayUtil;

    private static Handler handler;

    public static void main(String[] args) {
        resolveArgs(args);
        AsyncHttpServer httpServer = new AsyncHttpServer() {

            @Override
            protected boolean onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) {
                return super.onRequest(request, response);
            }
        };
        Looper.prepare();
        looper = Looper.myLooper();
        System.out.println(">>> DroidCast main entry");
        handler = new Handler(looper);
        displayUtil = new DisplayUtil();
        AsyncServer server = new AsyncServer();
        httpServer.get("/screenshot", new AnyRequestCallback());
        httpServer.websocket("/src", (webSocket, request) -> {
            Pair<Integer, Integer> pair = getDimension();
            displayUtil.setRotateListener(rotate -> {
                System.out.println(">>> rotate to " + rotate);
                // delay for the new rotated screen
                handler.postDelayed(() -> {
                    Pair<Integer, Integer> dimen = getDimension();
                    sendScreenshotData(webSocket, dimen.first, dimen.second);
                }, SCREENSHOT_DELAY_MILLIS);
            });
            sendScreenshotData(webSocket, pair.first, pair.second);
        });
        httpServer.listen(server, port);
        Looper.loop();
    }

    private static void resolveArgs(String[] args) {
        if (args.length > 0) {
            String[] params = args[0].split("=");
            if ("--port".equals(params[0])) {
                try {
                    port = Integer.parseInt(params[1]);
                    System.out.println(sTAG + " | Port set to " + port);
                } catch (NumberFormatException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @NonNull
    private static Pair<Integer, Integer> getDimension() {
        Point displaySize = displayUtil.getCurrentDisplaySize();
        int width = 1080;
        int height = 1920;
        if (displaySize != null) {
            width = displaySize.x;
            height = displaySize.y;
        }
        return new Pair<>(width, height);
    }

    private static void sendScreenshotData(WebSocket webSocket, int width, int height) {
        try {
            byte[] inBytes = getScreenImageInBytes(Bitmap.CompressFormat.JPEG, width, height, (w, h, rotation) -> {
                JSONObject obj = new JSONObject();
                try {
                    obj.put("width", w);
                    obj.put("height", h);
                    obj.put("rotation", rotation);
                    webSocket.send(obj.toString());
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            });
            webSocket.send(inBytes);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static byte[] getScreenImageInBytes(Bitmap.CompressFormat compressFormat, int w, int h, @Nullable ImageDimensionListener resolver) throws IOException {
        int destWidth = w;
        int destHeight = h;
        Bitmap bitmap = ScreenCaptorUtils.screenshot(destWidth, destHeight);
        if (bitmap == null) {
            System.out.println(String.format(Locale.ENGLISH, ">>> failed to generate image with resolution %d:%d", Main.width, Main.height));
            destWidth /= 2;
            destHeight /= 2;
            bitmap = ScreenCaptorUtils.screenshot(destWidth, destHeight);
        }
        System.out.println(String.format(Locale.ENGLISH, "Bitmap generated with resolution %d:%d, process id %d | thread id %d", destWidth, destHeight, Process.myPid(), Process.myTid()));
        int screenRotation = displayUtil.getScreenRotation();
        if (screenRotation != 0) {
            switch(screenRotation) {
                case // 90 degree rotation (counter-clockwise)
                1:
                    bitmap = displayUtil.rotateBitmap(bitmap, -90f);
                    break;
                case // 270 degree rotation
                3:
                    bitmap = displayUtil.rotateBitmap(bitmap, 90f);
                    break;
                case // 180 degree rotation
                2:
                    bitmap = displayUtil.rotateBitmap(bitmap, 180f);
                    break;
                default:
                    break;
            }
        }
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        System.out.println("Bitmap final dimens : " + width + "|" + height);
        if (resolver != null) {
            resolver.onResolveDimension(width, height, screenRotation);
        }
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        bitmap.compress(compressFormat, 100, bout);
        bout.flush();
        // "Make sure to call Bitmap.recycle() as soon as possible, once its content is not
        // needed anymore."
        bitmap.recycle();
        return bout.toByteArray();
    }

    interface ImageDimensionListener {

        void onResolveDimension(int width, int height, int rotation);
    }

    static clreplaced AnyRequestCallback implements HttpServerRequestCallback {

        private Pair<Bitmap.CompressFormat, String> mapRequestFormatInfo(ImageFormat imageFormat) {
            Bitmap.CompressFormat compressFormat;
            String contentType;
            switch(imageFormat) {
                case JPEG:
                    compressFormat = Bitmap.CompressFormat.JPEG;
                    contentType = IMAGE_JPEG;
                    break;
                case PNG:
                    compressFormat = Bitmap.CompressFormat.PNG;
                    contentType = IMAGE_PNG;
                    break;
                case WEBP:
                    compressFormat = Bitmap.CompressFormat.WEBP;
                    contentType = IMAGE_WEBP;
                    break;
                default:
                    throw new UnsupportedOperationException("Unsupported image format detected");
            }
            return new Pair<>(compressFormat, contentType);
        }

        @Nullable
        private Pair<Bitmap.CompressFormat, String> getImageFormatInfo(String reqFormat) {
            ImageFormat format = ImageFormat.JPEG;
            if (!TextUtils.isEmpty(reqFormat)) {
                ImageFormat imageFormat = ImageFormat.resolveFormat(reqFormat);
                if (ImageFormat.UNKNOWN.equals(imageFormat)) {
                    return null;
                } else {
                    // default format
                    format = imageFormat;
                }
            }
            return mapRequestFormatInfo(format);
        }

        @Override
        public void onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) {
            try {
                Multimap pairs = request.getQuery();
                String width = pairs.getString(WIDTH);
                String height = pairs.getString(HEIGHT);
                String reqFormat = pairs.getString(FORMAT);
                Pair<Bitmap.CompressFormat, String> formatInfo = getImageFormatInfo(reqFormat);
                if (formatInfo == null) {
                    response.code(400);
                    response.send(String.format(Locale.ENGLISH, "Unsupported image format : %s", reqFormat));
                    return;
                }
                if (!TextUtils.isEmpty(width) && !TextUtils.isEmpty(height) && TextUtils.isDigitsOnly(width) && TextUtils.isDigitsOnly(height)) {
                    Main.width = Integer.parseInt(width);
                    Main.height = Integer.parseInt(height);
                }
                if (Main.width == 0 || Main.height == 0) {
                    // dimension initialization
                    Point point = displayUtil.getCurrentDisplaySize();
                    if (point != null && point.x > 0 && point.y > 0) {
                        Main.width = point.x;
                        Main.height = point.y;
                    } else {
                        Main.width = 480;
                        Main.height = 800;
                    }
                }
                int destWidth = Main.width;
                int destHeight = Main.height;
                byte[] bytes = getScreenImageInBytes(formatInfo.first, destWidth, destHeight, null);
                response.send(formatInfo.second, bytes);
            } catch (Exception e) {
                response.code(500);
                response.send(e.toString());
            }
        }
    }
}

19 Source : ThreadPool.java
with MIT License
from ravindu1024

/**
 * Initialize the http thread pool
 *
 * @param num number of threads in thread pool
 */
static void initialize(int num) throws InterruptedException {
    if (mInstance == null) {
        mInstance = new ThreadPool();
        mInstance.mThreads = new HandlerThread[num];
        for (int i = 0; i < num; i++) {
            mInstance.mThreads[i] = new HandlerThread("Velocity_workerThread_" + i);
            mInstance.mThreads[i].start();
            int count = 0;
            while (mInstance.mThreads[i].getLooper() == null && count++ < 10) {
                Thread.sleep(50);
            }
            Looper l = mInstance.mThreads[i].getLooper();
            if (mInstance.mThreads[i].getLooper() != null) {
                mInstance.mHandlers.add(new Handler(l));
            }
        }
        NetLog.d("initialized threadpool with size : " + num);
    }
}

19 Source : FakeClock.java
with Apache License 2.0
from PaulWoitaschek

@Override
public HandlerWrapper createHandler(Looper looper, Callback callback) {
    return new ClockHandler(looper, callback);
}

19 Source : DefaultDrmSessionManager.java
with Apache License 2.0
from PaulWoitaschek

private void maybeCreateMediaDrmHandler(Looper playbackLooper) {
    if (mediaDrmHandler == null) {
        mediaDrmHandler = new MediaDrmHandler(playbackLooper);
    }
}

19 Source : DefaultDrmSessionManager.java
with Apache License 2.0
from PaulWoitaschek

// Internal methods.
private void replacedertExpectedPlaybackLooper(Looper playbackLooper) {
    replacedertions.checkState(this.playbackLooper == null || this.playbackLooper == playbackLooper);
    this.playbackLooper = playbackLooper;
}

19 Source : AndroidSchedulers.java
with MIT License
from nurkiewicz

/**
 * A {@link Scheduler} which executes actions on {@code looper}.
 */
public static Scheduler from(Looper looper) {
    if (looper == null)
        throw new NullPointerException("looper == null");
    return new LooperScheduler(looper);
}

19 Source : NonStopIntentService.java
with GNU General Public License v3.0
from NearbyShops

/**
 * Created by sumeet on 9/3/17.
 */
public abstract clreplaced NonStopIntentService extends Service {

    private String mName;

    private volatile Looper mServiceLooper;

    private volatile ServiceHandler mServiceHandler;

    public NonStopIntentService(String name) {
        super();
        mName = name;
    }

    private final clreplaced ServiceHandler extends Handler {

        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent) msg.obj);
        // stopSelf(msg.arg1); <-- Removed
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }

    /**
     * This method is invoked on the worker thread with a request to process.
     * Only one Intent is processed at a time, but the processing happens on a
     * worker thread that runs independently from other application logic.
     * So, if this code takes a long time, it will hold up other requests to
     * the same IntentService, but it will not hold up anything else.
     *
     * @param intent The value preplaceded to {@link
     *               android.content.Context#startService(Intent)}.
     */
    protected abstract void onHandleIntent(Intent intent);
}

19 Source : ForegroundService.java
with GNU General Public License v3.0
from MuntashirAkon

public abstract clreplaced ForegroundService extends Service {

    private final String name;

    private Looper serviceLooper;

    private ServiceHandler serviceHandler;

    // Handler that receives messages from the thread
    private final clreplaced ServiceHandler extends Handler {

        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent(msg.getData().getParcelable("intent"));
            stopSelf(msg.arg1);
        }
    }

    protected ForegroundService(String name) {
        this.name = name;
    }

    @Override
    public void onCreate() {
        HandlerThread thread = new HandlerThread(name, Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();
        serviceLooper = thread.getLooper();
        serviceHandler = new ServiceHandler(serviceLooper);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Message msg = serviceHandler.obtainMessage();
        msg.arg1 = startId;
        Bundle args = new Bundle();
        args.putParcelable("intent", intent);
        msg.setData(args);
        serviceHandler.sendMessage(msg);
        return START_NOT_STICKY;
    }

    protected abstract void onHandleIntent(@Nullable Intent intent);

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

19 Source : MapboxFusedLocationEngineImplTest.java
with MIT License
from mapbox

@Test
public void requestLocationUpdatesOutdoors() {
    LocationEngineRequest request = new LocationEngineRequest.Builder(10).setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY).build();
    LocationEngineCallback<LocationEngineResult> callback = mock(LocationEngineCallback.clreplaced);
    Looper looper = mock(Looper.clreplaced);
    when(locationManagerMock.getBestProvider(any(Criteria.clreplaced), anyBoolean())).thenReturn("gps");
    engine.requestLocationUpdates(request, callback, looper);
    verify(locationManagerMock, times(2)).requestLocationUpdates(anyString(), anyLong(), anyFloat(), any(LocationListener.clreplaced), any(Looper.clreplaced));
}

19 Source : MapboxFusedLocationEngineImplTest.java
with MIT License
from mapbox

@Test
public void requestLocationUpdatesIndoors() {
    LocationEngineRequest request = new LocationEngineRequest.Builder(10).setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY).build();
    LocationEngineCallback<LocationEngineResult> callback = mock(LocationEngineCallback.clreplaced);
    Looper looper = mock(Looper.clreplaced);
    when(locationManagerMock.getBestProvider(any(Criteria.clreplaced), anyBoolean())).thenReturn("network");
    engine.requestLocationUpdates(request, callback, looper);
    verify(locationManagerMock, times(1)).requestLocationUpdates(anyString(), anyLong(), anyFloat(), any(LocationListener.clreplaced), any(Looper.clreplaced));
}

19 Source : AndroidLocationEngineImplTest.java
with MIT License
from mapbox

@Test
public void requestLocationUpdatesBestProviderNull() {
    LocationEngineRequest request = new LocationEngineRequest.Builder(10).setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY).build();
    LocationEngineCallback<LocationEngineResult> callback = mock(LocationEngineCallback.clreplaced);
    Looper looper = mock(Looper.clreplaced);
    when(locationManagerMock.getBestProvider(any(Criteria.clreplaced), anyBoolean())).thenReturn(null);
    engine.requestLocationUpdates(request, callback, looper);
    replacedertThat(androidLocationEngineImpl.currentProvider).isEqualTo("preplacedive");
}

19 Source : LocationEngineProxy.java
with MIT License
from mapbox

@Override
public void requestLocationUpdates(@NonNull LocationEngineRequest request, @NonNull LocationEngineCallback<LocationEngineResult> callback, @Nullable Looper looper) throws SecurityException {
    checkNotNull(request, "request == null");
    checkNotNull(callback, "callback == null");
    locationEngineImpl.requestLocationUpdates(request, getListener(callback), looper == null ? Looper.getMainLooper() : looper);
}

19 Source : AsyncQueryHandler.java
with Apache License 2.0
from Madrapps

/**
 * A helper clreplaced to help make handling asynchronous {@link ContentResolver}
 * queries easier.
 */
public abstract clreplaced AsyncQueryHandler extends Handler {

    private static final String TAG = "AsyncQuery";

    private static final boolean localLOGV = false;

    private static final int EVENT_ARG_QUERY = 1;

    private static final int EVENT_ARG_INSERT = 2;

    private static final int EVENT_ARG_UPDATE = 3;

    private static final int EVENT_ARG_DELETE = 4;

    private static final int EVENT_ARG_BULK_INSERT = 5;

    private static Looper sLooper = null;

    private final WeakReference<ContentResolver> mResolver;

    private Handler mWorkerThreadHandler;

    public AsyncQueryHandler(ContentResolver cr) {
        super();
        mResolver = new WeakReference<>(cr);
        synchronized (AsyncQueryHandler.clreplaced) {
            if (sLooper == null) {
                HandlerThread thread = new HandlerThread("AsyncQueryWorker");
                thread.start();
                sLooper = thread.getLooper();
            }
        }
        mWorkerThreadHandler = createHandler(sLooper);
    }

    protected Handler createHandler(Looper looper) {
        return new WorkerHandler(looper);
    }

    /**
     * This method begins an asynchronous query. When the query is done
     * {@link #onQueryComplete} is called.
     *
     * @param token         A token preplaceded into {@link #onQueryComplete} to identify
     *                      the query.
     * @param cookie        An object that gets preplaceded into {@link #onQueryComplete}
     * @param uri           The URI, using the content:// scheme, for the content to
     *                      retrieve.
     * @param projection    A list of which columns to return. Preplaceding null will
     *                      return all columns, which is discouraged to prevent reading data
     *                      from storage that isn't going to be used.
     * @param selection     A filter declaring which rows to return, formatted as an
     *                      SQL WHERE clause (excluding the WHERE itself). Preplaceding null will
     *                      return all rows for the given URI.
     * @param selectionArgs You may include ?s in selection, which will be
     *                      replaced by the values from selectionArgs, in the order that they
     *                      appear in the selection. The values will be bound as Strings.
     * @param orderBy       How to order the rows, formatted as an SQL ORDER BY
     *                      clause (excluding the ORDER BY itself). Preplaceding null will use the
     *                      default sort order, which may be unordered.
     */
    public void startQuery(int token, Object cookie, Uri uri, String[] projection, String selection, String[] selectionArgs, String orderBy) {
        // Use the token as what so cancelOperations works properly
        Message msg = mWorkerThreadHandler.obtainMessage(token);
        msg.arg1 = EVENT_ARG_QUERY;
        WorkerArgs args = new WorkerArgs();
        args.handler = this;
        args.uri = uri;
        args.projection = projection;
        args.selection = selection;
        args.selectionArgs = selectionArgs;
        args.orderBy = orderBy;
        args.cookie = cookie;
        msg.obj = args;
        mWorkerThreadHandler.sendMessage(msg);
    }

    /**
     * Attempts to cancel operation that has not already started. Note that
     * there is no guarantee that the operation will be canceled. They still may
     * result in a call to on[Query/Insert/Update/Delete]Complete after this
     * call has completed.
     *
     * @param token The token representing the operation to be canceled.
     *              If multiple operations have the same token they will all be canceled.
     */
    public final void cancelOperation(int token) {
        mWorkerThreadHandler.removeMessages(token);
    }

    /**
     * This method begins an asynchronous insert. When the insert operation is
     * done {@link #onInsertComplete} is called.
     *
     * @param token         A token preplaceded into {@link #onInsertComplete} to identify
     *                      the insert operation.
     * @param cookie        An object that gets preplaceded into {@link #onInsertComplete}
     * @param uri           the Uri preplaceded to the insert operation.
     * @param initialValues the ContentValues parameter preplaceded to the insert operation.
     */
    public final void startInsert(int token, Object cookie, Uri uri, ContentValues initialValues) {
        // Use the token as what so cancelOperations works properly
        Message msg = mWorkerThreadHandler.obtainMessage(token);
        msg.arg1 = EVENT_ARG_INSERT;
        WorkerArgs args = new WorkerArgs();
        args.handler = this;
        args.uri = uri;
        args.cookie = cookie;
        args.values = initialValues;
        msg.obj = args;
        mWorkerThreadHandler.sendMessage(msg);
    }

    /**
     * This method begins an asynchronous bulk insert. When the insert operation is
     * done {@link #onBulkInsertComplete} is called.
     *
     * @param token         A token preplaceded into {@link #onBulkInsertComplete} to identify
     *                      the insert operation.
     * @param cookie        An object that gets preplaceded into {@link #onBulkInsertComplete}
     * @param uri           the Uri preplaceded to the insert operation.
     * @param initialValues the ContentValues array parameter preplaceded to the insert operation.
     */
    public final void startBulkInsert(int token, Object cookie, Uri uri, ContentValues[] initialValues) {
        // Use the token as what so cancelOperations works properly
        Message msg = mWorkerThreadHandler.obtainMessage(token);
        msg.arg1 = EVENT_ARG_BULK_INSERT;
        WorkerArgs args = new WorkerArgs();
        args.handler = this;
        args.uri = uri;
        args.cookie = cookie;
        args.valuesArray = initialValues;
        msg.obj = args;
        mWorkerThreadHandler.sendMessage(msg);
    }

    /**
     * This method begins an asynchronous update. When the update operation is
     * done {@link #onUpdateComplete} is called.
     *
     * @param token  A token preplaceded into {@link #onUpdateComplete} to identify
     *               the update operation.
     * @param cookie An object that gets preplaceded into {@link #onUpdateComplete}
     * @param uri    the Uri preplaceded to the update operation.
     * @param values the ContentValues parameter preplaceded to the update operation.
     */
    public final void startUpdate(int token, Object cookie, Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        // Use the token as what so cancelOperations works properly
        Message msg = mWorkerThreadHandler.obtainMessage(token);
        msg.arg1 = EVENT_ARG_UPDATE;
        WorkerArgs args = new WorkerArgs();
        args.handler = this;
        args.uri = uri;
        args.cookie = cookie;
        args.values = values;
        args.selection = selection;
        args.selectionArgs = selectionArgs;
        msg.obj = args;
        mWorkerThreadHandler.sendMessage(msg);
    }

    /**
     * This method begins an asynchronous delete. When the delete operation is
     * done {@link #onDeleteComplete} is called.
     *
     * @param token     A token preplaceded into {@link #onDeleteComplete} to identify
     *                  the delete operation.
     * @param cookie    An object that gets preplaceded into {@link #onDeleteComplete}
     * @param uri       the Uri preplaceded to the delete operation.
     * @param selection the where clause.
     */
    public final void startDelete(int token, Object cookie, Uri uri, String selection, String[] selectionArgs) {
        // Use the token as what so cancelOperations works properly
        Message msg = mWorkerThreadHandler.obtainMessage(token);
        msg.arg1 = EVENT_ARG_DELETE;
        WorkerArgs args = new WorkerArgs();
        args.handler = this;
        args.uri = uri;
        args.cookie = cookie;
        args.selection = selection;
        args.selectionArgs = selectionArgs;
        msg.obj = args;
        mWorkerThreadHandler.sendMessage(msg);
    }

    /**
     * Called when an asynchronous query is completed.
     *
     * @param token  the token to identify the query, preplaceded in from
     *               {@link #startQuery}.
     * @param cookie the cookie object preplaceded in from {@link #startQuery}.
     * @param cursor The cursor holding the results from the query.
     */
    protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
    // Empty
    }

    /**
     * Called when an asynchronous insert is completed.
     *
     * @param token  the token to identify the query, preplaceded in from
     *               {@link #startInsert}.
     * @param cookie the cookie object that's preplaceded in from
     *               {@link #startInsert}.
     * @param uri    the uri returned from the insert operation.
     */
    protected void onInsertComplete(int token, Object cookie, Uri uri) {
    // Empty
    }

    /**
     * Called when an asynchronous insert is completed.
     *
     * @param token  the token to identify the query, preplaceded in from
     *               {@link #startInsert}.
     * @param cookie the cookie object that's preplaceded in from
     *               {@link #startInsert}.
     * @param result the result returned from the bulk insert operation
     */
    protected void onBulkInsertComplete(int token, Object cookie, int result) {
    // Empty
    }

    /**
     * Called when an asynchronous update is completed.
     *
     * @param token  the token to identify the query, preplaceded in from
     *               {@link #startUpdate}.
     * @param cookie the cookie object that's preplaceded in from
     *               {@link #startUpdate}.
     * @param result the result returned from the update operation
     */
    protected void onUpdateComplete(int token, Object cookie, int result) {
    // Empty
    }

    /**
     * Called when an asynchronous delete is completed.
     *
     * @param token  the token to identify the query, preplaceded in from
     *               {@link #startDelete}.
     * @param cookie the cookie object that's preplaceded in from
     *               {@link #startDelete}.
     * @param result the result returned from the delete operation
     */
    protected void onDeleteComplete(int token, Object cookie, int result) {
    // Empty
    }

    @Override
    public void handleMessage(Message msg) {
        WorkerArgs args = (WorkerArgs) msg.obj;
        if (localLOGV) {
            Log.d(TAG, "AsyncQueryHandler.handleMessage: msg.what=" + msg.what + ", msg.arg1=" + msg.arg1);
        }
        int token = msg.what;
        int event = msg.arg1;
        // preplaced token back to caller on each callback.
        switch(event) {
            case EVENT_ARG_QUERY:
                onQueryComplete(token, args.cookie, (Cursor) args.result);
                break;
            case EVENT_ARG_INSERT:
                onInsertComplete(token, args.cookie, (Uri) args.result);
                break;
            case EVENT_ARG_BULK_INSERT:
                onBulkInsertComplete(token, args.cookie, (Integer) args.result);
                break;
            case EVENT_ARG_UPDATE:
                onUpdateComplete(token, args.cookie, (Integer) args.result);
                break;
            case EVENT_ARG_DELETE:
                onDeleteComplete(token, args.cookie, (Integer) args.result);
                break;
        }
    }

    protected static final clreplaced WorkerArgs {

        public Uri uri;

        public Handler handler;

        public String[] projection;

        public String selection;

        public String[] selectionArgs;

        public String orderBy;

        public Object result;

        public Object cookie;

        public ContentValues values;

        public ContentValues[] valuesArray;
    }

    protected clreplaced WorkerHandler extends Handler {

        public WorkerHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            final ContentResolver resolver = mResolver.get();
            if (resolver == null)
                return;
            WorkerArgs args = (WorkerArgs) msg.obj;
            int token = msg.what;
            int event = msg.arg1;
            switch(event) {
                case EVENT_ARG_QUERY:
                    Cursor cursor;
                    try {
                        cursor = resolver.query(args.uri, args.projection, args.selection, args.selectionArgs, args.orderBy);
                        // Calling getCount() causes the cursor window to be filled,
                        // which will make the first access on the main thread a lot faster.
                        if (cursor != null) {
                            cursor.getCount();
                        }
                    } catch (Exception e) {
                        Log.w(TAG, "Exception thrown during handling EVENT_ARG_QUERY", e);
                        cursor = null;
                    }
                    args.result = cursor;
                    break;
                case EVENT_ARG_INSERT:
                    args.result = resolver.insert(args.uri, args.values);
                    break;
                case EVENT_ARG_BULK_INSERT:
                    args.result = resolver.bulkInsert(args.uri, args.valuesArray);
                    break;
                case EVENT_ARG_UPDATE:
                    args.result = resolver.update(args.uri, args.values, args.selection, args.selectionArgs);
                    break;
                case EVENT_ARG_DELETE:
                    args.result = resolver.delete(args.uri, args.selection, args.selectionArgs);
                    break;
            }
            // preplaceding the original token value back to the caller
            // on top of the event values in arg1.
            Message reply = args.handler.obtainMessage(token);
            reply.obj = args;
            reply.arg1 = msg.arg1;
            if (localLOGV) {
                Log.d(TAG, "WorkerHandler.handleMsg: msg.arg1=" + msg.arg1 + ", reply.what=" + reply.what);
            }
            reply.sendToTarget();
        }
    }
}

19 Source : AsyncQueryHandler.java
with Apache License 2.0
from Madrapps

protected Handler createHandler(Looper looper) {
    return new WorkerHandler(looper);
}

19 Source : WalletService.java
with Apache License 2.0
from m2049r

@Override
public void onCreate() {
    // We are using a HandlerThread and a Looper to avoid loading and closing
    // concurrency
    MoneroHandlerThread thread = new MoneroHandlerThread("WalletService", Process.THREAD_PRIORITY_BACKGROUND);
    thread.start();
    // Get the HandlerThread's Looper and use it for our Handler
    final Looper serviceLooper = thread.getLooper();
    mServiceHandler = new WalletService.ServiceHandler(serviceLooper);
    Timber.d("Service created");
}

19 Source : MoneroHandlerThread.java
with Apache License 2.0
from m2049r

/**
 * Handy clreplaced for starting a new thread that has a looper. The looper can then be
 * used to create handler clreplacedes. Note that start() must still be called.
 * The started Thread has a stck size of STACK_SIZE (=5MB)
 */
public clreplaced MoneroHandlerThread extends Thread {

    // from src/cryptonote_config.h
    static public final long THREAD_STACK_SIZE = 5 * 1024 * 1024;

    private int mPriority;

    private int mTid = -1;

    private Looper mLooper;

    public MoneroHandlerThread(String name) {
        super(null, null, name, THREAD_STACK_SIZE);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    /**
     * Constructs a MoneroHandlerThread.
     *
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from
     *                 {@link android.os.Process} and not from java.lang.Thread.
     */
    MoneroHandlerThread(String name, int priority) {
        super(null, null, name, THREAD_STACK_SIZE);
        mPriority = priority;
    }

    /**
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */
    private void onLooperPrepared() {
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

    /**
     * This method returns the Looper replacedociated with this thread. If this thread not been started
     * or for any reason is isAlive() returns false, this method will return null. If this thread
     * has been started, this method will block until the looper has been initialized.
     *
     * @return The looper.
     */
    Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

    /**
     * Quits the handler thread's looper.
     * <p>
     * Causes the handler thread's looper to terminate without processing any
     * more messages in the message queue.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p><p clreplaced="note">
     * Using this method may be unsafe because some messages may not be delivered
     * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
     * that all pending work is completed in an orderly manner.
     * </p>
     *
     * @return True if the looper looper has been asked to quit or false if the
     * thread had not yet started running.
     * @see #quitSafely
     */
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }

    /**
     * Quits the handler thread's looper safely.
     * <p>
     * Causes the handler thread's looper to terminate as soon as all remaining messages
     * in the message queue that are already due to be delivered have been handled.
     * Pending delayed messages with due times in the future will not be delivered.
     * </p><p>
     * Any attempt to post messages to the queue after the looper is asked to quit will fail.
     * For example, the {@link Handler#sendMessage(Message)} method will return false.
     * </p><p>
     * If the thread has not been started or has finished (that is if
     * {@link #getLooper} returns null), then false is returned.
     * Otherwise the looper is asked to quit and true is returned.
     * </p>
     *
     * @return True if the looper looper has been asked to quit or false if the
     * thread had not yet started running.
     */
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

    /**
     * Returns the identifier of this thread. See Process.myTid().
     */
    public int getThreadId() {
        return mTid;
    }
}

19 Source : MoneroHandlerThread.java
with Apache License 2.0
from m2049r

/**
 * Quits the handler thread's looper.
 * <p>
 * Causes the handler thread's looper to terminate without processing any
 * more messages in the message queue.
 * </p><p>
 * Any attempt to post messages to the queue after the looper is asked to quit will fail.
 * For example, the {@link Handler#sendMessage(Message)} method will return false.
 * </p><p clreplaced="note">
 * Using this method may be unsafe because some messages may not be delivered
 * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
 * that all pending work is completed in an orderly manner.
 * </p>
 *
 * @return True if the looper looper has been asked to quit or false if the
 * thread had not yet started running.
 * @see #quitSafely
 */
public boolean quit() {
    Looper looper = getLooper();
    if (looper != null) {
        looper.quit();
        return true;
    }
    return false;
}

19 Source : MoneroHandlerThread.java
with Apache License 2.0
from m2049r

/**
 * Quits the handler thread's looper safely.
 * <p>
 * Causes the handler thread's looper to terminate as soon as all remaining messages
 * in the message queue that are already due to be delivered have been handled.
 * Pending delayed messages with due times in the future will not be delivered.
 * </p><p>
 * Any attempt to post messages to the queue after the looper is asked to quit will fail.
 * For example, the {@link Handler#sendMessage(Message)} method will return false.
 * </p><p>
 * If the thread has not been started or has finished (that is if
 * {@link #getLooper} returns null), then false is returned.
 * Otherwise the looper is asked to quit and true is returned.
 * </p>
 *
 * @return True if the looper looper has been asked to quit or false if the
 * thread had not yet started running.
 */
public boolean quitSafely() {
    Looper looper = getLooper();
    if (looper != null) {
        looper.quitSafely();
        return true;
    }
    return false;
}

19 Source : InputMonitor.java
with Apache License 2.0
from lulululbj

WindowManagerPolicy.InputConsumer createInputConsumer(Looper looper, String name, InputEventReceiver.Factory inputEventReceiverFactory) {
    if (mInputConsumers.containsKey(name)) {
        throw new IllegalStateException("Existing input consumer found with name: " + name);
    }
    final EventReceiverInputConsumer consumer = new EventReceiverInputConsumer(mService, this, looper, name, inputEventReceiverFactory, Process.myPid(), UserHandle.SYSTEM);
    addInputConsumer(name, consumer);
    return consumer;
}

19 Source : HdmiCecFeatureAction.java
with Apache License 2.0
from lulululbj

private ActionTimer createActionTimer(Looper looper) {
    return new ActionTimerHandler(looper);
}

19 Source : Choreographer.java
with Apache License 2.0
from lulululbj

/**
 * Coordinates the timing of animations, input and drawing.
 * <p>
 * The creplacedographer receives timing pulses (such as vertical synchronization)
 * from the display subsystem then schedules work to occur as part of rendering
 * the next display frame.
 * </p><p>
 * Applications typically interact with the creplacedographer indirectly using
 * higher level abstractions in the animation framework or the view hierarchy.
 * Here are some examples of things you can do using the higher-level APIs.
 * </p>
 * <ul>
 * <li>To post an animation to be processed on a regular time basis synchronized with
 * display frame rendering, use {@link android.animation.ValueAnimator#start}.</li>
 * <li>To post a {@link Runnable} to be invoked once at the beginning of the next display
 * frame, use {@link View#postOnAnimation}.</li>
 * <li>To post a {@link Runnable} to be invoked once at the beginning of the next display
 * frame after a delay, use {@link View#postOnAnimationDelayed}.</li>
 * <li>To post a call to {@link View#invalidate()} to occur once at the beginning of the
 * next display frame, use {@link View#postInvalidateOnAnimation()} or
 * {@link View#postInvalidateOnAnimation(int, int, int, int)}.</li>
 * <li>To ensure that the contents of a {@link View} scroll smoothly and are drawn in
 * sync with display frame rendering, do nothing.  This already happens automatically.
 * {@link View#onDraw} will be called at the appropriate time.</li>
 * </ul>
 * <p>
 * However, there are a few cases where you might want to use the functions of the
 * creplacedographer directly in your application.  Here are some examples.
 * </p>
 * <ul>
 * <li>If your application does its rendering in a different thread, possibly using GL,
 * or does not use the animation framework or view hierarchy at all
 * and you want to ensure that it is appropriately synchronized with the display, then use
 * {@link Creplacedographer#postFrameCallback}.</li>
 * <li>... and that's about it.</li>
 * </ul>
 * <p>
 * Each {@link Looper} thread has its own creplacedographer.  Other threads can
 * post callbacks to run on the creplacedographer but they will run on the {@link Looper}
 * to which the creplacedographer belongs.
 * </p>
 */
public final clreplaced Creplacedographer {

    private static final String TAG = "Creplacedographer";

    // Prints debug messages about jank which was detected (low volume).
    private static final boolean DEBUG_JANK = false;

    // Prints debug messages about every frame and callback registered (high volume).
    private static final boolean DEBUG_FRAMES = false;

    // The default amount of time in ms between animation frames.
    // When vsync is not enabled, we want to have some idea of how long we should
    // wait before posting the next animation message.  It is important that the
    // default value be less than the true inter-frame delay on all devices to avoid
    // situations where we might skip frames by waiting too long (we must compensate
    // for jitter and hardware variations).  Regardless of this value, the animation
    // and display loop is ultimately rate-limited by how fast new graphics buffers can
    // be dequeued.
    private static final long DEFAULT_FRAME_DELAY = 10;

    // The number of milliseconds between animation frames.
    private static volatile long sFrameDelay = DEFAULT_FRAME_DELAY;

    // Thread local storage for the creplacedographer.
    private static final ThreadLocal<Creplacedographer> sThreadInstance = new ThreadLocal<Creplacedographer>() {

        @Override
        protected Creplacedographer initialValue() {
            Looper looper = Looper.myLooper();
            if (looper == null) {
                throw new IllegalStateException("The current thread must have a looper!");
            }
            Creplacedographer creplacedographer = new Creplacedographer(looper, VSYNC_SOURCE_APP);
            if (looper == Looper.getMainLooper()) {
                mMainInstance = creplacedographer;
            }
            return creplacedographer;
        }
    };

    private static volatile Creplacedographer mMainInstance;

    // Thread local storage for the SF creplacedographer.
    private static final ThreadLocal<Creplacedographer> sSfThreadInstance = new ThreadLocal<Creplacedographer>() {

        @Override
        protected Creplacedographer initialValue() {
            Looper looper = Looper.myLooper();
            if (looper == null) {
                throw new IllegalStateException("The current thread must have a looper!");
            }
            return new Creplacedographer(looper, VSYNC_SOURCE_SURFACE_FLINGER);
        }
    };

    // Enable/disable vsync for animations and drawing.
    private static final boolean USE_VSYNC = SystemProperties.getBoolean("debug.creplacedographer.vsync", true);

    // Enable/disable using the frame time instead of returning now.
    private static final boolean USE_FRAME_TIME = SystemProperties.getBoolean("debug.creplacedographer.frametime", true);

    // Set a limit to warn about skipped frames.
    // Skipped frames imply jank.
    private static final int SKIPPED_FRAME_WARNING_LIMIT = SystemProperties.getInt("debug.creplacedographer.skipwarning", 30);

    private static final int MSG_DO_FRAME = 0;

    private static final int MSG_DO_SCHEDULE_VSYNC = 1;

    private static final int MSG_DO_SCHEDULE_CALLBACK = 2;

    // All frame callbacks posted by applications have this token.
    private static final Object FRAME_CALLBACK_TOKEN = new Object() {

        public String toString() {
            return "FRAME_CALLBACK_TOKEN";
        }
    };

    private final Object mLock = new Object();

    private final Looper mLooper;

    private final FrameHandler mHandler;

    // The display event receiver can only be accessed by the looper thread to which
    // it is attached.  We take care to ensure that we post message to the looper
    // if appropriate when interacting with the display event receiver.
    private final FrameDisplayEventReceiver mDisplayEventReceiver;

    private CallbackRecord mCallbackPool;

    private final CallbackQueue[] mCallbackQueues;

    private boolean mFrameScheduled;

    private boolean mCallbacksRunning;

    private long mLastFrameTimeNanos;

    private long mFrameIntervalNanos;

    private boolean mDebugPrintNextFrameTimeDelta;

    private int mFPSDivisor = 1;

    /**
     * Contains information about the current frame for jank-tracking,
     * mainly timings of key events along with a bit of metadata about
     * view tree state
     *
     * TODO: Is there a better home for this? Currently Creplacedographer
     * is the only one with CALLBACK_ANIMATION start time, hence why this
     * resides here.
     *
     * @hide
     */
    FrameInfo mFrameInfo = new FrameInfo();

    /**
     * Must be kept in sync with CALLBACK_* ints below, used to index into this array.
     * @hide
     */
    private static final String[] CALLBACK_TRACE_replacedLES = { "input", "animation", "traversal", "commit" };

    /**
     * Callback type: Input callback.  Runs first.
     * @hide
     */
    public static final int CALLBACK_INPUT = 0;

    /**
     * Callback type: Animation callback.  Runs before traversals.
     * @hide
     */
    @TestApi
    public static final int CALLBACK_ANIMATION = 1;

    /**
     * Callback type: Traversal callback.  Handles layout and draw.  Runs
     * after all other asynchronous messages have been handled.
     * @hide
     */
    public static final int CALLBACK_TRAVERSAL = 2;

    /**
     * Callback type: Commit callback.  Handles post-draw operations for the frame.
     * Runs after traversal completes.  The {@link #getFrameTime() frame time} reported
     * during this callback may be updated to reflect delays that occurred while
     * traversals were in progress in case heavy layout operations caused some frames
     * to be skipped.  The frame time reported during this callback provides a better
     * estimate of the start time of the frame in which animations (and other updates
     * to the view hierarchy state) actually took effect.
     * @hide
     */
    public static final int CALLBACK_COMMIT = 3;

    private static final int CALLBACK_LAST = CALLBACK_COMMIT;

    private Creplacedographer(Looper looper, int vsyncSource) {
        mLooper = looper;
        // 处理事件
        mHandler = new FrameHandler(looper);
        // 4.1 之后默认为 true,
        // FrameDisplayEventReceiver 是个 vsync 事件接收器
        mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper, vsyncSource) : null;
        mLastFrameTimeNanos = Long.MIN_VALUE;
        // 一帧的时间,60pfs 的话就是 16.7ms
        mFrameIntervalNanos = (long) (1000000000 / getRefreshRate());
        // 回调队列
        mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
        for (int i = 0; i <= CALLBACK_LAST; i++) {
            mCallbackQueues[i] = new CallbackQueue();
        }
        // b/68769804: For low FPS experiments.
        setFPSDivisor(SystemProperties.getInt(ThreadedRenderer.DEBUG_FPS_DIVISOR, 1));
    }

    private static float getRefreshRate() {
        DisplayInfo di = DisplayManagerGlobal.getInstance().getDisplayInfo(Display.DEFAULT_DISPLAY);
        return di.getMode().getRefreshRate();
    }

    /**
     * Gets the creplacedographer for the calling thread.  Must be called from
     * a thread that already has a {@link android.os.Looper} replacedociated with it.
     *
     * @return The creplacedographer for this thread.
     * 当前线程一般就是主线程
     * @throws IllegalStateException if the thread does not have a looper.
     */
    public static Creplacedographer getInstance() {
        return sThreadInstance.get();
    }

    /**
     * @hide
     */
    public static Creplacedographer getSfInstance() {
        return sSfThreadInstance.get();
    }

    /**
     * @return The Creplacedographer of the main thread, if it exists, or {@code null} otherwise.
     * @hide
     */
    public static Creplacedographer getMainThreadInstance() {
        return mMainInstance;
    }

    /**
     * Destroys the calling thread's creplacedographer
     * @hide
     */
    public static void releaseInstance() {
        Creplacedographer old = sThreadInstance.get();
        sThreadInstance.remove();
        old.dispose();
    }

    private void dispose() {
        mDisplayEventReceiver.dispose();
    }

    /**
     * The amount of time, in milliseconds, between each frame of the animation.
     * <p>
     * This is a requested time that the animation will attempt to honor, but the actual delay
     * between frames may be different, depending on system load and capabilities. This is a static
     * function because the same delay will be applied to all animations, since they are all
     * run off of a single timing loop.
     * </p><p>
     * The frame delay may be ignored when the animation system uses an external timing
     * source, such as the display refresh rate (vsync), to govern animations.
     * </p>
     *
     * @return the requested time between frames, in milliseconds
     * @hide
     */
    @TestApi
    public static long getFrameDelay() {
        return sFrameDelay;
    }

    /**
     * The amount of time, in milliseconds, between each frame of the animation.
     * <p>
     * This is a requested time that the animation will attempt to honor, but the actual delay
     * between frames may be different, depending on system load and capabilities. This is a static
     * function because the same delay will be applied to all animations, since they are all
     * run off of a single timing loop.
     * </p><p>
     * The frame delay may be ignored when the animation system uses an external timing
     * source, such as the display refresh rate (vsync), to govern animations.
     * </p>
     *
     * @param frameDelay the requested time between frames, in milliseconds
     * @hide
     */
    @TestApi
    public static void setFrameDelay(long frameDelay) {
        sFrameDelay = frameDelay;
    }

    /**
     * Subtracts typical frame delay time from a delay interval in milliseconds.
     * <p>
     * This method can be used to compensate for animation delay times that have baked
     * in replacedumptions about the frame delay.  For example, it's quite common for code to
     * replacedume a 60Hz frame time and bake in a 16ms delay.  When we call
     * {@link #postAnimationCallbackDelayed} we want to know how long to wait before
     * posting the animation callback but let the animation timer take care of the remaining
     * frame delay time.
     * </p><p>
     * This method is somewhat conservative about how much of the frame delay it
     * subtracts.  It uses the same value returned by {@link #getFrameDelay} which by
     * default is 10ms even though many parts of the system replacedume 16ms.  Consequently,
     * we might still wait 6ms before posting an animation callback that we want to run
     * on the next frame, but this is much better than waiting a whole 16ms and likely
     * missing the deadline.
     * </p>
     *
     * @param delayMillis The original delay time including an replacedumed frame delay.
     * @return The adjusted delay time with the replacedumed frame delay subtracted out.
     * @hide
     */
    public static long subtractFrameDelay(long delayMillis) {
        final long frameDelay = sFrameDelay;
        return delayMillis <= frameDelay ? 0 : delayMillis - frameDelay;
    }

    /**
     * @return The refresh rate as the nanoseconds between frames
     * @hide
     */
    public long getFrameIntervalNanos() {
        return mFrameIntervalNanos;
    }

    void dump(String prefix, PrintWriter writer) {
        String innerPrefix = prefix + "  ";
        writer.print(prefix);
        writer.println("Creplacedographer:");
        writer.print(innerPrefix);
        writer.print("mFrameScheduled=");
        writer.println(mFrameScheduled);
        writer.print(innerPrefix);
        writer.print("mLastFrameTime=");
        writer.println(TimeUtils.formatUptime(mLastFrameTimeNanos / 1000000));
    }

    /**
     * Posts a callback to run on the next frame.
     * <p>
     * The callback runs once then is automatically removed.
     * </p>
     *
     * @param callbackType The callback type.
     * @param action The callback action to run during the next frame.
     * @param token The callback token, or null if none.
     *
     * @see #removeCallbacks
     * @hide
     */
    @TestApi
    public void postCallback(int callbackType, Runnable action, Object token) {
        postCallbackDelayed(callbackType, action, token, 0);
    }

    /**
     * Posts a callback to run on the next frame after the specified delay.
     * <p>
     * The callback runs once then is automatically removed.
     * </p>
     *
     * @param callbackType The callback type.
     * @param action The callback action to run during the next frame after the specified delay.
     * @param token The callback token, or null if none.
     * @param delayMillis The delay time in milliseconds.
     *
     * @see #removeCallback
     * @hide
     */
    @TestApi
    public void postCallbackDelayed(int callbackType, Runnable action, Object token, long delayMillis) {
        if (action == null) {
            throw new IllegalArgumentException("action must not be null");
        }
        if (callbackType < 0 || callbackType > CALLBACK_LAST) {
            throw new IllegalArgumentException("callbackType is invalid");
        }
        postCallbackDelayedInternal(callbackType, action, token, delayMillis);
    }

    private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) {
        if (DEBUG_FRAMES) {
            Log.d(TAG, "PostCallback: type=" + callbackType + ", action=" + action + ", token=" + token + ", delayMillis=" + delayMillis);
        }
        synchronized (mLock) {
            final long now = SystemClock.uptimeMillis();
            final long dueTime = now + delayMillis;
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
            if (dueTime <= now) {
                // 立即执行
                scheduleFrameLocked(now);
            } else {
                // 延迟执行
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, dueTime);
            }
        }
    }

    /**
     * Removes callbacks that have the specified action and token.
     *
     * @param callbackType The callback type.
     * @param action The action property of the callbacks to remove, or null to remove
     * callbacks with any action.
     * @param token The token property of the callbacks to remove, or null to remove
     * callbacks with any token.
     *
     * @see #postCallback
     * @see #postCallbackDelayed
     * @hide
     */
    @TestApi
    public void removeCallbacks(int callbackType, Runnable action, Object token) {
        if (callbackType < 0 || callbackType > CALLBACK_LAST) {
            throw new IllegalArgumentException("callbackType is invalid");
        }
        removeCallbacksInternal(callbackType, action, token);
    }

    private void removeCallbacksInternal(int callbackType, Object action, Object token) {
        if (DEBUG_FRAMES) {
            Log.d(TAG, "RemoveCallbacks: type=" + callbackType + ", action=" + action + ", token=" + token);
        }
        synchronized (mLock) {
            mCallbackQueues[callbackType].removeCallbacksLocked(action, token);
            if (action != null && token == null) {
                mHandler.removeMessages(MSG_DO_SCHEDULE_CALLBACK, action);
            }
        }
    }

    /**
     * Posts a frame callback to run on the next frame.
     * <p>
     * The callback runs once then is automatically removed.
     * </p>
     *
     * @param callback The frame callback to run during the next frame.
     *
     * @see #postFrameCallbackDelayed
     * @see #removeFrameCallback
     */
    public void postFrameCallback(FrameCallback callback) {
        postFrameCallbackDelayed(callback, 0);
    }

    /**
     * Posts a frame callback to run on the next frame after the specified delay.
     * <p>
     * The callback runs once then is automatically removed.
     * </p>
     *
     * @param callback The frame callback to run during the next frame.
     * @param delayMillis The delay time in milliseconds.
     *
     * @see #postFrameCallback
     * @see #removeFrameCallback
     */
    public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
        if (callback == null) {
            throw new IllegalArgumentException("callback must not be null");
        }
        postCallbackDelayedInternal(CALLBACK_ANIMATION, callback, FRAME_CALLBACK_TOKEN, delayMillis);
    }

    /**
     * Removes a previously posted frame callback.
     *
     * @param callback The frame callback to remove.
     *
     * @see #postFrameCallback
     * @see #postFrameCallbackDelayed
     */
    public void removeFrameCallback(FrameCallback callback) {
        if (callback == null) {
            throw new IllegalArgumentException("callback must not be null");
        }
        removeCallbacksInternal(CALLBACK_ANIMATION, callback, FRAME_CALLBACK_TOKEN);
    }

    /**
     * Gets the time when the current frame started.
     * <p>
     * This method provides the time in milliseconds when the frame started being rendered.
     * The frame time provides a stable time base for synchronizing animations
     * and drawing.  It should be used instead of {@link SystemClock#uptimeMillis()}
     * or {@link System#nanoTime()} for animations and drawing in the UI.  Using the frame
     * time helps to reduce inter-frame jitter because the frame time is fixed at the time
     * the frame was scheduled to start, regardless of when the animations or drawing
     * callback actually runs.  All callbacks that run as part of rendering a frame will
     * observe the same frame time so using the frame time also helps to synchronize effects
     * that are performed by different callbacks.
     * </p><p>
     * Please note that the framework already takes care to process animations and
     * drawing using the frame time as a stable time base.  Most applications should
     * not need to use the frame time information directly.
     * </p><p>
     * This method should only be called from within a callback.
     * </p>
     *
     * @return The frame start time, in the {@link SystemClock#uptimeMillis()} time base.
     *
     * @throws IllegalStateException if no frame is in progress.
     * @hide
     */
    public long getFrameTime() {
        return getFrameTimeNanos() / TimeUtils.NANOS_PER_MS;
    }

    /**
     * Same as {@link #getFrameTime()} but with nanosecond precision.
     *
     * @return The frame start time, in the {@link System#nanoTime()} time base.
     *
     * @throws IllegalStateException if no frame is in progress.
     * @hide
     */
    public long getFrameTimeNanos() {
        synchronized (mLock) {
            if (!mCallbacksRunning) {
                throw new IllegalStateException("This method must only be called as " + "part of a callback while a frame is in progress.");
            }
            return USE_FRAME_TIME ? mLastFrameTimeNanos : System.nanoTime();
        }
    }

    /**
     * Like {@link #getLastFrameTimeNanos}, but always returns the last frame time, not matter
     * whether callbacks are currently running.
     * @return The frame start time of the last frame, in the {@link System#nanoTime()} time base.
     * @hide
     */
    public long getLastFrameTimeNanos() {
        synchronized (mLock) {
            return USE_FRAME_TIME ? mLastFrameTimeNanos : System.nanoTime();
        }
    }

    private void scheduleFrameLocked(long now) {
        if (!mFrameScheduled) {
            mFrameScheduled = true;
            if (USE_VSYNC) {
                if (DEBUG_FRAMES) {
                    Log.d(TAG, "Scheduling next frame on vsync.");
                }
                // If running on the Looper thread, then schedule the vsync immediately,
                // otherwise post a message to schedule the vsync from the UI thread
                // as soon as possible.
                // 如果是当前线程,直接申请 vsync,否则通过 handler 通信
                if (isRunningOnLooperThreadLocked()) {
                    scheduleVsyncLocked();
                } else {
                    Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtFrontOfQueue(msg);
                }
            } else {
                // 未开启 vsync,4.1 之后默认开启
                final long nextFrameTime = Math.max(mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
                if (DEBUG_FRAMES) {
                    Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
                }
                Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, nextFrameTime);
            }
        }
    }

    void setFPSDivisor(int divisor) {
        if (divisor <= 0)
            divisor = 1;
        mFPSDivisor = divisor;
        ThreadedRenderer.setFPSDivisor(divisor);
    }

    void doFrame(long frameTimeNanos, int frame) {
        final long startNanos;
        synchronized (mLock) {
            if (!mFrameScheduled) {
                // no work to do
                return;
            }
            if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) {
                mDebugPrintNextFrameTimeDelta = false;
                Log.d(TAG, "Frame time delta: " + ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + " ms");
            }
            long intendedFrameTimeNanos = frameTimeNanos;
            startNanos = System.nanoTime();
            // 计算超时时间
            // frameTimeNanos 是 vsync 信号回调的时间,startNanos 是当前时间戳
            // 相减得到主线程的耗时时间
            final long jitterNanos = startNanos - frameTimeNanos;
            // mFrameIntervalNanos 是一帧的时间
            if (jitterNanos >= mFrameIntervalNanos) {
                // 计算掉了多少帧
                final long skippedFrames = jitterNanos / mFrameIntervalNanos;
                // 掉帧超过 30 帧,打印 log
                if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
                    Log.i(TAG, "Skipped " + skippedFrames + " frames!  " + "The application may be doing too much work on its main thread.");
                }
                final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
                if (DEBUG_JANK) {
                    Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms " + "which is more than the frame interval of " + (mFrameIntervalNanos * 0.000001f) + " ms!  " + "Skipping " + skippedFrames + " frames and setting frame " + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
                }
                frameTimeNanos = startNanos - lastFrameOffset;
            }
            if (frameTimeNanos < mLastFrameTimeNanos) {
                if (DEBUG_JANK) {
                    Log.d(TAG, "Frame time appears to be going backwards.  May be due to a " + "previously skipped frame.  Waiting for next vsync.");
                }
                scheduleVsyncLocked();
                return;
            }
            if (mFPSDivisor > 1) {
                long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
                if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
                    scheduleVsyncLocked();
                    return;
                }
            }
            mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
            mFrameScheduled = false;
            mLastFrameTimeNanos = frameTimeNanos;
        }
        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Creplacedographer#doFrame");
            AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
            // doCallBacks() 开始执行回调
            mFrameInfo.markInputHandlingStart();
            doCallbacks(Creplacedographer.CALLBACK_INPUT, frameTimeNanos);
            mFrameInfo.markAnimationsStart();
            doCallbacks(Creplacedographer.CALLBACK_ANIMATION, frameTimeNanos);
            mFrameInfo.markPerformTraversalsStart();
            doCallbacks(Creplacedographer.CALLBACK_TRAVERSAL, frameTimeNanos);
            doCallbacks(Creplacedographer.CALLBACK_COMMIT, frameTimeNanos);
        } finally {
            AnimationUtils.unlockAnimationClock();
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        if (DEBUG_FRAMES) {
            final long endNanos = System.nanoTime();
            Log.d(TAG, "Frame " + frame + ": Finished, took " + (endNanos - startNanos) * 0.000001f + " ms, latency " + (startNanos - frameTimeNanos) * 0.000001f + " ms.");
        }
    }

    void doCallbacks(int callbackType, long frameTimeNanos) {
        CallbackRecord callbacks;
        synchronized (mLock) {
            // We use "now" to determine when callbacks become due because it's possible
            // for earlier processing phases in a frame to post callbacks that should run
            // in a following phase, such as an input event that causes an animation to start.
            final long now = System.nanoTime();
            // 根据 callbackType 找到对应的 CallbackRecord 对象
            callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now / TimeUtils.NANOS_PER_MS);
            if (callbacks == null) {
                return;
            }
            mCallbacksRunning = true;
            // Update the frame time if necessary when committing the frame.
            // We only update the frame time if we are more than 2 frames late reaching
            // the commit phase.  This ensures that the frame time which is observed by the
            // callbacks will always increase from one frame to the next and never repeat.
            // We never want the next frame's starting frame time to end up being less than
            // or equal to the previous frame's commit frame time.  Keep in mind that the
            // next frame has most likely already been scheduled by now so we play it
            // safe by ensuring the commit time is always at least one frame behind.
            if (callbackType == Creplacedographer.CALLBACK_COMMIT) {
                final long jitterNanos = now - frameTimeNanos;
                Trace.traceCounter(Trace.TRACE_TAG_VIEW, "jitterNanos", (int) jitterNanos);
                if (jitterNanos >= 2 * mFrameIntervalNanos) {
                    final long lastFrameOffset = jitterNanos % mFrameIntervalNanos + mFrameIntervalNanos;
                    if (DEBUG_JANK) {
                        Log.d(TAG, "Commit callback delayed by " + (jitterNanos * 0.000001f) + " ms which is more than twice the frame interval of " + (mFrameIntervalNanos * 0.000001f) + " ms!  " + "Setting frame time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
                        mDebugPrintNextFrameTimeDelta = true;
                    }
                    frameTimeNanos = now - lastFrameOffset;
                    mLastFrameTimeNanos = frameTimeNanos;
                }
            }
        }
        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_replacedLES[callbackType]);
            for (CallbackRecord c = callbacks; c != null; c = c.next) {
                if (DEBUG_FRAMES) {
                    Log.d(TAG, "RunCallback: type=" + callbackType + ", action=" + c.action + ", token=" + c.token + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
                }
                c.run(frameTimeNanos);
            }
        } finally {
            synchronized (mLock) {
                mCallbacksRunning = false;
                do {
                    final CallbackRecord next = callbacks.next;
                    recycleCallbackLocked(callbacks);
                    callbacks = next;
                } while (callbacks != null);
            }
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

    void doScheduleVsync() {
        synchronized (mLock) {
            if (mFrameScheduled) {
                scheduleVsyncLocked();
            }
        }
    }

    void doScheduleCallback(int callbackType) {
        synchronized (mLock) {
            if (!mFrameScheduled) {
                final long now = SystemClock.uptimeMillis();
                if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
                    scheduleFrameLocked(now);
                }
            }
        }
    }

    private void scheduleVsyncLocked() {
        mDisplayEventReceiver.scheduleVsync();
    }

    private boolean isRunningOnLooperThreadLocked() {
        return Looper.myLooper() == mLooper;
    }

    private CallbackRecord obtainCallbackLocked(long dueTime, Object action, Object token) {
        CallbackRecord callback = mCallbackPool;
        if (callback == null) {
            callback = new CallbackRecord();
        } else {
            mCallbackPool = callback.next;
            callback.next = null;
        }
        callback.dueTime = dueTime;
        callback.action = action;
        callback.token = token;
        return callback;
    }

    private void recycleCallbackLocked(CallbackRecord callback) {
        callback.action = null;
        callback.token = null;
        callback.next = mCallbackPool;
        mCallbackPool = callback;
    }

    /**
     * Implement this interface to receive a callback when a new display frame is
     * being rendered.  The callback is invoked on the {@link Looper} thread to
     * which the {@link Creplacedographer} is attached.
     */
    public interface FrameCallback {

        /**
         * Called when a new display frame is being rendered.
         * <p>
         * This method provides the time in nanoseconds when the frame started being rendered.
         * The frame time provides a stable time base for synchronizing animations
         * and drawing.  It should be used instead of {@link SystemClock#uptimeMillis()}
         * or {@link System#nanoTime()} for animations and drawing in the UI.  Using the frame
         * time helps to reduce inter-frame jitter because the frame time is fixed at the time
         * the frame was scheduled to start, regardless of when the animations or drawing
         * callback actually runs.  All callbacks that run as part of rendering a frame will
         * observe the same frame time so using the frame time also helps to synchronize effects
         * that are performed by different callbacks.
         * </p><p>
         * Please note that the framework already takes care to process animations and
         * drawing using the frame time as a stable time base.  Most applications should
         * not need to use the frame time information directly.
         * </p>
         *
         * @param frameTimeNanos The time in nanoseconds when the frame started being rendered,
         * in the {@link System#nanoTime()} timebase.  Divide this value by {@code 1000000}
         * to convert it to the {@link SystemClock#uptimeMillis()} time base.
         */
        public void doFrame(long frameTimeNanos);
    }

    private final clreplaced FrameHandler extends Handler {

        public FrameHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch(msg.what) {
                case MSG_DO_FRAME:
                    doFrame(System.nanoTime(), 0);
                    break;
                case MSG_DO_SCHEDULE_VSYNC:
                    // 申请 vsync 信号
                    doScheduleVsync();
                    break;
                case MSG_DO_SCHEDULE_CALLBACK:
                    // 执行延时消息
                    doScheduleCallback(msg.arg1);
                    break;
            }
        }
    }

    private final clreplaced FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable {

        private boolean mHavePendingVsync;

        private long mTimestampNanos;

        private int mFrame;

        public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
            super(looper, vsyncSource);
        }

        @Override
        public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
            // Ignore vsync from secondary display.
            // This can be problematic because the call to scheduleVsync() is a one-shot.
            // We need to ensure that we will still receive the vsync from the primary
            // display which is the one we really care about.  Ideally we should schedule
            // vsync for a particular display.
            // At this time Surface Flinger won't send us vsyncs for secondary displays
            // but that could change in the future so let's log a message to help us remember
            // that we need to fix this.
            if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
                Log.d(TAG, "Received vsync from secondary display, but we don't support " + "this case yet.  Creplacedographer needs a way to explicitly request " + "vsync for a specific display to ensure it doesn't lose track " + "of its scheduled vsync.");
                scheduleVsync();
                return;
            }
            // Post the vsync event to the Handler.
            // The idea is to prevent incoming vsync events from completely starving
            // the message queue.  If there are no messages in the queue with timestamps
            // earlier than the frame time, then the vsync event will be processed immediately.
            // Otherwise, messages that predate the vsync event will be handled first.
            long now = System.nanoTime();
            if (timestampNanos > now) {
                Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f) + " ms in the future!  Check that graphics HAL is generating vsync " + "timestamps using the correct timebase.");
                timestampNanos = now;
            }
            if (mHavePendingVsync) {
                Log.w(TAG, "Already have a pending vsync event.  There should only be " + "one at a time.");
            } else {
                mHavePendingVsync = true;
            }
            mTimestampNanos = timestampNanos;
            mFrame = frame;
            // 这里传入的是 this,会回调本身的 run() 方法
            Message msg = Message.obtain(mHandler, this);
            // 这是一个异步消息,保证优先执行
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }

        @Override
        public void run() {
            mHavePendingVsync = false;
            doFrame(mTimestampNanos, mFrame);
        }
    }

    private static final clreplaced CallbackRecord {

        public CallbackRecord next;

        public long dueTime;

        // Runnable or FrameCallback
        public Object action;

        public Object token;

        public void run(long frameTimeNanos) {
            if (token == FRAME_CALLBACK_TOKEN) {
                ((FrameCallback) action).doFrame(frameTimeNanos);
            } else {
                ((Runnable) action).run();
            }
        }
    }

    private final clreplaced CallbackQueue {

        private CallbackRecord mHead;

        public boolean hasDueCallbacksLocked(long now) {
            return mHead != null && mHead.dueTime <= now;
        }

        public CallbackRecord extractDueCallbacksLocked(long now) {
            CallbackRecord callbacks = mHead;
            if (callbacks == null || callbacks.dueTime > now) {
                return null;
            }
            CallbackRecord last = callbacks;
            CallbackRecord next = last.next;
            while (next != null) {
                if (next.dueTime > now) {
                    last.next = null;
                    break;
                }
                last = next;
                next = next.next;
            }
            mHead = next;
            return callbacks;
        }

        public void addCallbackLocked(long dueTime, Object action, Object token) {
            CallbackRecord callback = obtainCallbackLocked(dueTime, action, token);
            CallbackRecord entry = mHead;
            if (entry == null) {
                mHead = callback;
                return;
            }
            if (dueTime < entry.dueTime) {
                callback.next = entry;
                mHead = callback;
                return;
            }
            while (entry.next != null) {
                if (dueTime < entry.next.dueTime) {
                    callback.next = entry.next;
                    break;
                }
                entry = entry.next;
            }
            entry.next = callback;
        }

        public void removeCallbacksLocked(Object action, Object token) {
            CallbackRecord predecessor = null;
            for (CallbackRecord callback = mHead; callback != null; ) {
                final CallbackRecord next = callback.next;
                if ((action == null || callback.action == action) && (token == null || callback.token == token)) {
                    if (predecessor != null) {
                        predecessor.next = next;
                    } else {
                        mHead = next;
                    }
                    recycleCallbackLocked(callback);
                } else {
                    predecessor = callback;
                }
                callback = next;
            }
        }
    }
}

19 Source : AccessibilityManager.java
with Apache License 2.0
from lulululbj

/**
 * Requests feedback interruption from all accessibility services.
 */
public void interrupt() {
    final IAccessibilityManager service;
    final int userId;
    synchronized (mLock) {
        service = getServiceLocked();
        if (service == null) {
            return;
        }
        if (!isEnabled()) {
            Looper myLooper = Looper.myLooper();
            if (myLooper == Looper.getMainLooper()) {
                throw new IllegalStateException("Accessibility off. Did you forget to check that?");
            } else {
                // If we're not running on the thread with the main looper, it's possible for
                // the state of accessibility to change between checking isEnabled and
                // calling this method. So just log the error rather than throwing the
                // exception.
                Log.e(LOG_TAG, "Interrupt called with accessibility disabled");
                return;
            }
        }
        userId = mUserId;
    }
    try {
        service.interrupt(userId);
        if (DEBUG) {
            Log.i(LOG_TAG, "Requested interrupt from all services");
        }
    } catch (RemoteException re) {
        Log.e(LOG_TAG, "Error while requesting interrupt from all services. ", re);
    }
}

19 Source : AsyncQueryHandler.java
with Apache License 2.0
from lulululbj

/**
 * A helper clreplaced to help make handling asynchronous {@link ContentResolver}
 * queries easier.
 */
public abstract clreplaced AsyncQueryHandler extends Handler {

    private static final String TAG = "AsyncQuery";

    private static final boolean localLOGV = false;

    private static final int EVENT_ARG_QUERY = 1;

    private static final int EVENT_ARG_INSERT = 2;

    private static final int EVENT_ARG_UPDATE = 3;

    private static final int EVENT_ARG_DELETE = 4;

    /* package */
    final WeakReference<ContentResolver> mResolver;

    private static Looper sLooper = null;

    private Handler mWorkerThreadHandler;

    protected static final clreplaced WorkerArgs {

        public Uri uri;

        public Handler handler;

        public String[] projection;

        public String selection;

        public String[] selectionArgs;

        public String orderBy;

        public Object result;

        public Object cookie;

        public ContentValues values;
    }

    protected clreplaced WorkerHandler extends Handler {

        public WorkerHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            final ContentResolver resolver = mResolver.get();
            if (resolver == null)
                return;
            WorkerArgs args = (WorkerArgs) msg.obj;
            int token = msg.what;
            int event = msg.arg1;
            switch(event) {
                case EVENT_ARG_QUERY:
                    Cursor cursor;
                    try {
                        cursor = resolver.query(args.uri, args.projection, args.selection, args.selectionArgs, args.orderBy);
                        // Calling getCount() causes the cursor window to be filled,
                        // which will make the first access on the main thread a lot faster.
                        if (cursor != null) {
                            cursor.getCount();
                        }
                    } catch (Exception e) {
                        Log.w(TAG, "Exception thrown during handling EVENT_ARG_QUERY", e);
                        cursor = null;
                    }
                    args.result = cursor;
                    break;
                case EVENT_ARG_INSERT:
                    args.result = resolver.insert(args.uri, args.values);
                    break;
                case EVENT_ARG_UPDATE:
                    args.result = resolver.update(args.uri, args.values, args.selection, args.selectionArgs);
                    break;
                case EVENT_ARG_DELETE:
                    args.result = resolver.delete(args.uri, args.selection, args.selectionArgs);
                    break;
            }
            // preplaceding the original token value back to the caller
            // on top of the event values in arg1.
            Message reply = args.handler.obtainMessage(token);
            reply.obj = args;
            reply.arg1 = msg.arg1;
            if (localLOGV) {
                Log.d(TAG, "WorkerHandler.handleMsg: msg.arg1=" + msg.arg1 + ", reply.what=" + reply.what);
            }
            reply.sendToTarget();
        }
    }

    public AsyncQueryHandler(ContentResolver cr) {
        super();
        mResolver = new WeakReference<ContentResolver>(cr);
        synchronized (AsyncQueryHandler.clreplaced) {
            if (sLooper == null) {
                HandlerThread thread = new HandlerThread("AsyncQueryWorker");
                thread.start();
                sLooper = thread.getLooper();
            }
        }
        mWorkerThreadHandler = createHandler(sLooper);
    }

    protected Handler createHandler(Looper looper) {
        return new WorkerHandler(looper);
    }

    /**
     * This method begins an asynchronous query. When the query is done
     * {@link #onQueryComplete} is called.
     *
     * @param token A token preplaceded into {@link #onQueryComplete} to identify
     *  the query.
     * @param cookie An object that gets preplaceded into {@link #onQueryComplete}
     * @param uri The URI, using the content:// scheme, for the content to
     *         retrieve.
     * @param projection A list of which columns to return. Preplaceding null will
     *         return all columns, which is discouraged to prevent reading data
     *         from storage that isn't going to be used.
     * @param selection A filter declaring which rows to return, formatted as an
     *         SQL WHERE clause (excluding the WHERE itself). Preplaceding null will
     *         return all rows for the given URI.
     * @param selectionArgs You may include ?s in selection, which will be
     *         replaced by the values from selectionArgs, in the order that they
     *         appear in the selection. The values will be bound as Strings.
     * @param orderBy How to order the rows, formatted as an SQL ORDER BY
     *         clause (excluding the ORDER BY itself). Preplaceding null will use the
     *         default sort order, which may be unordered.
     */
    public void startQuery(int token, Object cookie, Uri uri, String[] projection, String selection, String[] selectionArgs, String orderBy) {
        // Use the token as what so cancelOperations works properly
        Message msg = mWorkerThreadHandler.obtainMessage(token);
        msg.arg1 = EVENT_ARG_QUERY;
        WorkerArgs args = new WorkerArgs();
        args.handler = this;
        args.uri = uri;
        args.projection = projection;
        args.selection = selection;
        args.selectionArgs = selectionArgs;
        args.orderBy = orderBy;
        args.cookie = cookie;
        msg.obj = args;
        mWorkerThreadHandler.sendMessage(msg);
    }

    /**
     * Attempts to cancel operation that has not already started. Note that
     * there is no guarantee that the operation will be canceled. They still may
     * result in a call to on[Query/Insert/Update/Delete]Complete after this
     * call has completed.
     *
     * @param token The token representing the operation to be canceled.
     *  If multiple operations have the same token they will all be canceled.
     */
    public final void cancelOperation(int token) {
        mWorkerThreadHandler.removeMessages(token);
    }

    /**
     * This method begins an asynchronous insert. When the insert operation is
     * done {@link #onInsertComplete} is called.
     *
     * @param token A token preplaceded into {@link #onInsertComplete} to identify
     *  the insert operation.
     * @param cookie An object that gets preplaceded into {@link #onInsertComplete}
     * @param uri the Uri preplaceded to the insert operation.
     * @param initialValues the ContentValues parameter preplaceded to the insert operation.
     */
    public final void startInsert(int token, Object cookie, Uri uri, ContentValues initialValues) {
        // Use the token as what so cancelOperations works properly
        Message msg = mWorkerThreadHandler.obtainMessage(token);
        msg.arg1 = EVENT_ARG_INSERT;
        WorkerArgs args = new WorkerArgs();
        args.handler = this;
        args.uri = uri;
        args.cookie = cookie;
        args.values = initialValues;
        msg.obj = args;
        mWorkerThreadHandler.sendMessage(msg);
    }

    /**
     * This method begins an asynchronous update. When the update operation is
     * done {@link #onUpdateComplete} is called.
     *
     * @param token A token preplaceded into {@link #onUpdateComplete} to identify
     *  the update operation.
     * @param cookie An object that gets preplaceded into {@link #onUpdateComplete}
     * @param uri the Uri preplaceded to the update operation.
     * @param values the ContentValues parameter preplaceded to the update operation.
     */
    public final void startUpdate(int token, Object cookie, Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        // Use the token as what so cancelOperations works properly
        Message msg = mWorkerThreadHandler.obtainMessage(token);
        msg.arg1 = EVENT_ARG_UPDATE;
        WorkerArgs args = new WorkerArgs();
        args.handler = this;
        args.uri = uri;
        args.cookie = cookie;
        args.values = values;
        args.selection = selection;
        args.selectionArgs = selectionArgs;
        msg.obj = args;
        mWorkerThreadHandler.sendMessage(msg);
    }

    /**
     * This method begins an asynchronous delete. When the delete operation is
     * done {@link #onDeleteComplete} is called.
     *
     * @param token A token preplaceded into {@link #onDeleteComplete} to identify
     *  the delete operation.
     * @param cookie An object that gets preplaceded into {@link #onDeleteComplete}
     * @param uri the Uri preplaceded to the delete operation.
     * @param selection the where clause.
     */
    public final void startDelete(int token, Object cookie, Uri uri, String selection, String[] selectionArgs) {
        // Use the token as what so cancelOperations works properly
        Message msg = mWorkerThreadHandler.obtainMessage(token);
        msg.arg1 = EVENT_ARG_DELETE;
        WorkerArgs args = new WorkerArgs();
        args.handler = this;
        args.uri = uri;
        args.cookie = cookie;
        args.selection = selection;
        args.selectionArgs = selectionArgs;
        msg.obj = args;
        mWorkerThreadHandler.sendMessage(msg);
    }

    /**
     * Called when an asynchronous query is completed.
     *
     * @param token the token to identify the query, preplaceded in from
     *            {@link #startQuery}.
     * @param cookie the cookie object preplaceded in from {@link #startQuery}.
     * @param cursor The cursor holding the results from the query.
     */
    protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
    // Empty
    }

    /**
     * Called when an asynchronous insert is completed.
     *
     * @param token the token to identify the query, preplaceded in from
     *        {@link #startInsert}.
     * @param cookie the cookie object that's preplaceded in from
     *        {@link #startInsert}.
     * @param uri the uri returned from the insert operation.
     */
    protected void onInsertComplete(int token, Object cookie, Uri uri) {
    // Empty
    }

    /**
     * Called when an asynchronous update is completed.
     *
     * @param token the token to identify the query, preplaceded in from
     *        {@link #startUpdate}.
     * @param cookie the cookie object that's preplaceded in from
     *        {@link #startUpdate}.
     * @param result the result returned from the update operation
     */
    protected void onUpdateComplete(int token, Object cookie, int result) {
    // Empty
    }

    /**
     * Called when an asynchronous delete is completed.
     *
     * @param token the token to identify the query, preplaceded in from
     *        {@link #startDelete}.
     * @param cookie the cookie object that's preplaceded in from
     *        {@link #startDelete}.
     * @param result the result returned from the delete operation
     */
    protected void onDeleteComplete(int token, Object cookie, int result) {
    // Empty
    }

    @Override
    public void handleMessage(Message msg) {
        WorkerArgs args = (WorkerArgs) msg.obj;
        if (localLOGV) {
            Log.d(TAG, "AsyncQueryHandler.handleMessage: msg.what=" + msg.what + ", msg.arg1=" + msg.arg1);
        }
        int token = msg.what;
        int event = msg.arg1;
        // preplaced token back to caller on each callback.
        switch(event) {
            case EVENT_ARG_QUERY:
                onQueryComplete(token, args.cookie, (Cursor) args.result);
                break;
            case EVENT_ARG_INSERT:
                onInsertComplete(token, args.cookie, (Uri) args.result);
                break;
            case EVENT_ARG_UPDATE:
                onUpdateComplete(token, args.cookie, (Integer) args.result);
                break;
            case EVENT_ARG_DELETE:
                onDeleteComplete(token, args.cookie, (Integer) args.result);
                break;
        }
    }
}

19 Source : IntentService.java
with Apache License 2.0
from lulululbj

/**
 * IntentService is a base clreplaced for {@link Service}s that handle asynchronous
 * requests (expressed as {@link Intent}s) on demand.  Clients send requests
 * through {@link android.content.Context#startService(Intent)} calls; the
 * service is started as needed, handles each Intent in turn using a worker
 * thread, and stops itself when it runs out of work.
 *
 * <p>This "work queue processor" pattern is commonly used to offload tasks
 * from an application's main thread.  The IntentService clreplaced exists to
 * simplify this pattern and take care of the mechanics.  To use it, extend
 * IntentService and implement {@link #onHandleIntent(Intent)}.  IntentService
 * will receive the Intents, launch a worker thread, and stop the service as
 * appropriate.
 *
 * <p>All requests are handled on a single worker thread -- they may take as
 * long as necessary (and will not block the application's main loop), but
 * only one request will be processed at a time.
 *
 * <p clreplaced="note"><b>Note:</b> IntentService is subject to all the
 * <a href="/preview/features/background.html">background execution limits</a>
 * imposed with Android 8.0 (API level 26). In most cases, you are better off
 * using {@link android.support.v4.app.JobIntentService}, which uses jobs
 * instead of services when running on Android 8.0 or higher.
 * </p>
 *
 * <div clreplaced="special reference">
 * <h3>Developer Guides</h3>
 * <p>For a detailed discussion about how to create services, read the
 * <a href="{@docRoot}guide/components/services.html">Services</a> developer
 * guide.</p>
 * </div>
 *
 * @see android.support.v4.app.JobIntentService
 * @see android.os.AsyncTask
 */
public abstract clreplaced IntentService extends Service {

    private volatile Looper mServiceLooper;

    private volatile ServiceHandler mServiceHandler;

    private String mName;

    private boolean mRedelivery;

    private final clreplaced ServiceHandler extends Handler {

        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent) msg.obj);
            stopSelf(msg.arg1);
        }
    }

    /**
     * Creates an IntentService.  Invoked by your subclreplaced's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public IntentService(String name) {
        super();
        mName = name;
    }

    /**
     * Sets intent redelivery preferences.  Usually called from the constructor
     * with your preferred semantics.
     *
     * <p>If enabled is true,
     * {@link #onStartCommand(Intent, int, int)} will return
     * {@link Service#START_REDELIVER_INTENT}, so if this process dies before
     * {@link #onHandleIntent(Intent)} returns, the process will be restarted
     * and the intent redelivered.  If multiple Intents have been sent, only
     * the most recent one is guaranteed to be redelivered.
     *
     * <p>If enabled is false (the default),
     * {@link #onStartCommand(Intent, int, int)} will return
     * {@link Service#START_NOT_STICKY}, and if the process dies, the Intent
     * dies along with it.
     */
    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    @Override
    public void onCreate() {
        // TODO: It would be nice to have an option to hold a partial wakelock
        // during processing, and to have a static startService(Context, Intent)
        // method that would launch the service & hand off a wakelock.
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    /**
     * You should not override this method for your IntentService. Instead,
     * override {@link #onHandleIntent}, which the system calls when the IntentService
     * receives a start request.
     * @see android.app.Service#onStartCommand
     */
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }

    /**
     * Unless you provide binding for your service, you don't need to implement this
     * method, because the default implementation returns null.
     * @see android.app.Service#onBind
     */
    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }

    /**
     * This method is invoked on the worker thread with a request to process.
     * Only one Intent is processed at a time, but the processing happens on a
     * worker thread that runs independently from other application logic.
     * So, if this code takes a long time, it will hold up other requests to
     * the same IntentService, but it will not hold up anything else.
     * When all requests have been handled, the IntentService stops itself,
     * so you should not call {@link #stopSelf}.
     *
     * @param intent The value preplaceded to {@link
     *               android.content.Context#startService(Intent)}.
     *               This may be null if the service is being restarted after
     *               its process has gone away; see
     *               {@link android.app.Service#onStartCommand}
     *               for details.
     */
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

See More Examples