Android 从聊天头服务启动活动时出现延迟

Android 从聊天头服务启动活动时出现延迟,android,android-activity,service,Android,Android Activity,Service,我创建了一个应用程序,它有一个类似Facebook的聊天头服务。在聊天头上单击,我正在启动一项活动(请参见MyActivity)的代码)。如下文所述,我在特定场景中启动活动时会遇到延迟 点击聊天头;活动顺利展开 按主页按钮;按照我的逻辑,活动会破坏 现在,如果我再次单击chat head,它将以延迟启动活动 秒) 若我从最近的活动堆栈中清除活动,或者若我按下后退按钮,则不会出现此问题。即使我删除了dispatchKeyEvent功能,问题仍然存在 public class MyActivit

我创建了一个应用程序,它有一个类似Facebook的聊天头服务。在聊天头上单击,我正在启动一项活动(请参见
MyActivity
)的代码)。如下文所述,我在特定场景中启动活动时会遇到延迟

  • 点击聊天头;活动顺利展开
  • 按主页按钮;按照我的逻辑,活动会破坏
  • 现在,如果我再次单击chat head,它将以延迟启动活动 秒)
若我从最近的活动堆栈中清除活动,或者若我按下后退按钮,则不会出现此问题。即使我删除了
dispatchKeyEvent
功能,问题仍然存在

public class MyActivity extends AppCompatActivity implements ChatHeadListener{

    public static final String DESTROY = "destroy";
    public static final String MAXIMIZED = "MAXIMIZED";
    public static final String MINIMIZED = "MINIMIZED";
    public static final String STATE = "State";
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ChatHead.getInstance().setChatHeadListener(this);
        setContentView(R.layout.float_body);
        textView = (TextView) findViewById(R.id.root_text);
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK
                || event.getKeyCode() == KeyEvent.KEYCODE_HOME) {
            finish();
            return true;
        }
        return super.dispatchKeyEvent(event);
    }

    @Override
    protected void onStart() {
        super.onStart();
        Intent destroy = new Intent(DESTROY);
        destroy.putExtra(STATE, MAXIMIZED);
        LocalBroadcastManager.getInstance(this).sendBroadcast(destroy);
    }

    @Override
    protected void onDestroy() {
        Intent destroy = new Intent(DESTROY);
        destroy.putExtra(STATE, MINIMIZED);
        LocalBroadcastManager.getInstance(this).sendBroadcast(destroy);
        super.onDestroy();
    }

    @Override
    public void onChatHeadClick(HeadModel headModel) {
        //method from my interface
        if(textView!=null){
            textView.setText(headModel.getVisitorName()+"");
        }
    }
}
更新(2016年11月4日)

main活动

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    button_start = (Button) findViewById(R.id.button_start);
    button_stop = (Button) findViewById(R.id.button_stop);
    Intent intent = new Intent(this, MainActivity.class);
    PendingIntent resultPendingIntent = PendingIntent.getActivity(
            this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    Notification notification = ChatHead.createNotification(
            this, "Chat head", "Service Running",
            R.drawable.ic_android_red_500_48dp, resultPendingIntent);

    View maxView = LayoutInflater.from(this).inflate(R.layout.chat_head_recycler, null);
    View minView = LayoutInflater.from(this).inflate(R.layout.chat_head_view, null);

    chatHead = ChatHead.createInstance(this, maxView, minView, NOTIFICATION_ID,
            notification, new ChatHeadOrientationListener() {
        @Override
        public void beforeOrientationChange(ChatHead chatHead) {
            Toast.makeText(MainActivity.this, "Orientation Change Start",
                    Toast.LENGTH_SHORT).show();
        }

        @Override
        public void afterOrientationChange(ChatHead chatHead) {
            Toast.makeText(MainActivity.this, "Orientation Change End",
                    Toast.LENGTH_SHORT).show();
        }
    });

    button_start.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                startChatHeadForAboveAndroidL();
            } else {
                chatHead.startService();
            }
        }
    });

    button_stop.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            chatHead.stopService();
        }
    });
}

@TargetApi(Build.VERSION_CODES.M)
public void startChatHeadForAboveAndroidL() {
    if (!Settings.canDrawOverlays(this)) {
        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                Uri.parse("package:" + getPackageName()));
        startActivityForResult(intent, PERMISSION_REQUEST_CODE);
    } else {
        chatHead.startService();
    }
}
聊天室主任班

public class ChatHead{

    private final View maxView, minView;
    private final Context context;
    private final Notification notification;
    private final int notificationId;
    private static ChatHead chatHead;
    private static State state;
    private final ChatHeadOrientationListener chatHeadOrientationListener;
    private float ratioY = 0;
    private float oldWidth = 0;
    private float oldX = 0;
    private boolean confChange = false;
    private static ChatHeadListener chatHeadListener;
    private static final String TAG = "ChatHead";
    private FragmentTransaction fragmentTransaction;

    /**
     *
     * @return The maxView of the chatHead which is assigned through the
     * {@link #createInstance} method.
     */
    public View getMaxView() {
        return chatHead.maxView;
    }

    /**
     *
     * Creates a Singleton of the Floating Window
     *
     * @param context The application context
     * @param maxView The maxView View, upon clicking it the body is to be opened
     * @param notificationId The notificationId for your notification
     * @param notification The notification which is displayed for the foreground service
     * @param chatHeadOrientationListener The {@link ChatHeadOrientationListener} interface
     *                                    with callbacks which are called when orientation changes.
     * @return A Floating Window
     *
     */
    public static synchronized ChatHead createInstance(Context context, View maxView, View minView,
                                                       int notificationId, Notification notification,
                                                       ChatHeadOrientationListener
                                                               chatHeadOrientationListener) {
        if (chatHead == null) {
            chatHead = new ChatHead(context, maxView, minView, notificationId, notification,
                    chatHeadOrientationListener);
        }
        return chatHead;
    }

    /**
     *
     * Creates a Singleton of the Floating Window
     *
     * @param context The application context
     * @param maxView The maxView View, upon clicking it the body is to be opened
     * @param notificationId The notificationId for your notification
     * @param notification The notification which is displayed for the foreground service
     * @return A Floating Window
     *
     */
    public static synchronized ChatHead createInstance(Context context, View maxView, View minView,
                                                       int notificationId, Notification notification) {
        if (chatHead == null) {
            chatHead = new ChatHead(context, maxView, minView, notificationId,
                    notification, new ChatHeadOrientationListener() {
                @Override
                public void beforeOrientationChange(ChatHead chatHead) {
                    Log.d(TAG, "beforeOrientationChange");
                }

                @Override
                public void afterOrientationChange(ChatHead chatHead) {
                    Log.d(TAG, "afterOrientationChange");
                }
            });
        }
        return chatHead;
    }

    /**
     * @return The same instance of Floating Window, which has been created through
     * {@link #createInstance}. Don't call this method before createInstance
     */
    static synchronized ChatHead getInstance() {
        if (chatHead == null) {
            throw new NullPointerException("ChatHead not initialized! " +
                    "First call createInstance method, then to access " +
                    "ChatHead in any other class call getInstance()");
        }
        return chatHead;
    }

    private ChatHead(Context context, View maxView, View minView, int notificationId,
                     Notification notification, ChatHeadOrientationListener chatHeadOrientationListener) {
        this.maxView = maxView;
        this.minView = minView;
        this.context = context;
        this.notification = notification;
        this.notificationId = notificationId;
        this.chatHeadOrientationListener = chatHeadOrientationListener;
    }

    /**
     * Starts the service and adds it to the screen
     */
    public void startService() {
        Intent intent = new Intent(context, ChatHeadService.class);
        context.startService(intent);
    }

    /**
     * Stops the service and removes it from the screen
     */
    public void stopService() {
        Intent intent = new Intent(context, ChatHeadService.class);
        context.stopService(intent);
    }

    /**
     *
     * Helper method for notification creation.
     *
     * @param context
     * @param contentTitle
     * @param contentText
     * @param notificationIcon
     * @param contentIntent
     * @return Notification for the Service
     */
    public static Notification createNotification(Context context,
                                                  String contentTitle, String contentText,
                                                  int notificationIcon, PendingIntent contentIntent) {
        return new NotificationCompat.Builder(context)
                .setContentTitle(contentTitle)
                .setContentText(contentText)
                .setSmallIcon(notificationIcon)
                .setContentIntent(contentIntent).build();
    }

    public static class ChatHeadService extends Service {

        private WindowManager windowManager;
        private WindowManager.LayoutParams params;
        private LinearLayout mLinearLayout;
        GestureDetectorCompat gestureDetectorCompat;
        DisplayMetrics metrics;
        private boolean didFling;
        private int[] clickLocation = new int[2];
        private LocalBroadcastManager localBroadcastManager;
        private RecyclerView recyclerView;
        private RelativeLayout rProgress, rSingleHeadCon;

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

        @Override
        public void onConfigurationChanged(Configuration newConfig) {
            if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE
                    || newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
                int[] location = new int[2];
                mLinearLayout.getLocationOnScreen(location);
                chatHead.oldWidth = metrics.widthPixels;
                chatHead.confChange = true;
                chatHead.oldX = location[0];
                chatHead.ratioY = (float) (location[1]) / (float) metrics.heightPixels;
                chatHead.chatHeadOrientationListener.beforeOrientationChange(chatHead);
                chatHead.stopService();
                chatHead.startService();
                chatHead.chatHeadOrientationListener.afterOrientationChange(chatHead);
            }
        }

        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.d(TAG, "onStartCommand");
            metrics = new DisplayMetrics();
            windowManager.getDefaultDisplay().getMetrics(metrics);
            startForeground(chatHead.notificationId, chatHead.notification);
            return START_STICKY;
        }

        @Override
        public void onCreate() {
            super.onCreate();
            Log.d(TAG, "onCreate");
            localBroadcastManager = LocalBroadcastManager.getInstance(this);
            localBroadcastManager.registerReceiver(receiver,
                    new IntentFilter(MyActivity.DESTROY));
            recyclerView = (RecyclerView) chatHead.maxView.findViewById(R.id.rv_heads);
            recyclerView.setLayoutManager(new LinearLayoutManager(
                    this, LinearLayoutManager.HORIZONTAL, false));
            rProgress = (RelativeLayout) chatHead.minView.findViewById(R.id.head_progress);
            rSingleHeadCon = (RelativeLayout) chatHead.minView.findViewById(R.id.head_view_con);
            mLinearLayout = new LinearLayout(getApplicationContext());

            gestureDetectorCompat = new GestureDetectorCompat(
                    chatHead.context, new GestureDetector.SimpleOnGestureListener() {
                private int initialX;
                private int initialY;
                private float initialTouchX;
                private float initialTouchY;

                @Override
                public boolean onDown(MotionEvent event) {
                    Log.d(TAG, "onDown");
                    initialX = params.x;
                    initialY = params.y;
                    initialTouchX = event.getRawX();
                    initialTouchY = event.getRawY();
                    didFling = false;
                    return false;
                }

                @Override
                public void onShowPress(MotionEvent e) {
                    Log.d(TAG, "onShowPress");
                    chatHead.minView.setAlpha(0.8f);
                }

                @Override
                public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
                                        float distanceY) {
                    params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
                    params.width = WindowManager.LayoutParams.WRAP_CONTENT;
                    params.height = WindowManager.LayoutParams.WRAP_CONTENT;
                    params.x = (initialX + (int) ((e2.getRawX() - initialTouchX)));
                    params.y = (initialY + (int) ((e2.getRawY() - initialTouchY)));
                    windowManager.updateViewLayout(mLinearLayout, params);
                    return false;
                }

                @Override
                public boolean onSingleTapConfirmed(MotionEvent e) {
                    Log.e(TAG, "Logging ChatHead: onSingleTapConfirmed ");
                    state = State.LOADING;
                    rProgress.setVisibility(View.VISIBLE);
                    rSingleHeadCon.setVisibility(View.GONE);
                    chatHead.minView.getLocationOnScreen(clickLocation);
                    params.x = clickLocation[0];
                    params.y = clickLocation[1] - 36;
                    windowManager.updateViewLayout(mLinearLayout, params);
                    Intent intent = new Intent(ChatHeadService.this,MyActivity.class);
                    intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
                            | Intent.FLAG_ACTIVITY_NEW_TASK);
                    startActivity(intent);
                    return false;
                }

                @Override
                public boolean onDoubleTap(MotionEvent e) {
                    chatHead.stopService();
                    return false;
                }

                @Override
                public boolean onFling(MotionEvent e1, MotionEvent e2,
                                       float velocityX, float velocityY) {
                    Log.d(TAG, "onFling");
                    didFling = true;
                    int newX = params.x;
                    if (newX > (metrics.widthPixels / 2))
                        params.x = metrics.widthPixels;
                    else
                        params.x = 0;
                    windowManager.updateViewLayout(mLinearLayout, params);
                    return false;
                }
            });

            mLinearLayout.setOrientation(LinearLayout.VERTICAL);
            windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
            metrics = new DisplayMetrics();
            windowManager.getDefaultDisplay().getMetrics(metrics);
            params = new WindowManager.LayoutParams(
                    WindowManager.LayoutParams.WRAP_CONTENT,
                    WindowManager.LayoutParams.WRAP_CONTENT,
                    WindowManager.LayoutParams.TYPE_PHONE,
                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                    PixelFormat.TRANSLUCENT);
            params.gravity = Gravity.TOP | Gravity.LEFT;

            if (chatHead.confChange) {
                chatHead.confChange = false;
                if (chatHead.oldX < (chatHead.oldWidth / 2)) {
                    params.x = 0;
                } else {
                    params.x = metrics.widthPixels;
                }
                params.y = (int) (metrics.heightPixels * chatHead.ratioY);
            } else {
                params.x = metrics.widthPixels;
                params.y = 0;
            }
            chatHead.minView.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    gestureDetectorCompat.onTouchEvent(event);
                    if (event.getAction() == MotionEvent.ACTION_UP) {
                        chatHead.minView.setAlpha(1.0f);
                        if (!didFling) {
                            Log.d(TAG, "ACTION_UP");
                            int newX = params.x;
                            if (newX > (metrics.widthPixels / 2)) {
                                params.x = metrics.widthPixels;
                            }else {
                                params.x = 0;
                                windowManager.updateViewLayout(mLinearLayout, params);
                            }
                        }
                    }
                    return true;
                }
            });

            windowManager.addView(mLinearLayout, params);
            mLinearLayout.setFocusable(true);
            LinearLayout.LayoutParams headParams = new LinearLayout.LayoutParams(
                    LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
            headParams.gravity = Gravity.TOP | Gravity.RIGHT;
            mLinearLayout.addView(chatHead.minView, headParams);
        }

        private void minimiseChatHead() {
            state = State.MINIMIZED;
            rProgress.setVisibility(View.GONE);
            rSingleHeadCon.setVisibility(View.VISIBLE);
            params.x = clickLocation[0];
            params.y = clickLocation[1] - 36;
            params.width = WindowManager.LayoutParams.WRAP_CONTENT;
            params.height = WindowManager.LayoutParams.WRAP_CONTENT;

            mLinearLayout.setFocusable(true);
            mLinearLayout.removeAllViews();
            LinearLayout.LayoutParams headParams = new LinearLayout.LayoutParams(
                    LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
            headParams.gravity = Gravity.TOP | Gravity.RIGHT;
            mLinearLayout.addView(chatHead.minView, headParams);
            mLinearLayout.setTag("IMAGEVIEW_TAG");

            //TODO close activity
            windowManager.updateViewLayout(mLinearLayout, params);
        }

        private void maximiseChatHead() {
            state = State.MAXIMIZED;
            params.x = metrics.widthPixels;
            params.y = 0;
//          params.flags = params.flags & ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
            params.width = WindowManager.LayoutParams.MATCH_PARENT;
            params.height = WindowManager.LayoutParams.WRAP_CONTENT;
//          chatHead.body.setVisibility(View.VISIBLE);
            List<HeadModel> heads = new ArrayList<>();
            heads.add(new HeadModel("chat1"));
            heads.add(new HeadModel("chat2"));
            heads.add(new HeadModel("chat3"));
            final ChatHeadsAdapter chatHeadsAdapter = new ChatHeadsAdapter(chatHead.context, heads);
            recyclerView.setAdapter(chatHeadsAdapter);

            chatHeadsAdapter.setOnLinkClickListener(new ChatHeadsAdapter.OnLinkClickListener() {
                @Override
                public void onSingleTapListener(final HeadModel headModel, final int position) {
                    if(state==State.MAXIMIZED){
                        if(chatHeadListener!=null){
                            chatHeadListener.onChatHeadClick(headModel);
                        }else{
                            maximiseChatHead();
                        }
                    }else if(state==State.MINIMIZED){
                        maximiseChatHead();
                    }
                }

                @Override
                public void onDoubleTapListener(final HeadModel headModel, final int position) {
                    chatHeadsAdapter.getHeads().remove(position);
                    chatHeadsAdapter.notifyItemRemoved(position);
                }
            });

//          mLinearLayout.setFocusable(true);
            mLinearLayout.removeAllViews();
            LinearLayout.LayoutParams headParams = new LinearLayout.LayoutParams(
                    LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
            headParams.gravity = Gravity.TOP | Gravity.RIGHT;
            mLinearLayout.addView(chatHead.maxView, headParams);

            windowManager.updateViewLayout(mLinearLayout, params);
        }

        public void onDestroy() {
            super.onDestroy();
            localBroadcastManager.unregisterReceiver(receiver);
            if (mLinearLayout != null) {
                mLinearLayout.removeAllViews();
                windowManager.removeView(mLinearLayout);
            }
            stopForeground(true);
        }

        private final BroadcastReceiver receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String bCast = intent.getStringExtra(MyActivity.STATE);
                Log.e(TAG, "Logging Chathead: onReceive "+bCast);
                if(bCast.equals(MyActivity.MAXIMIZED)){
                    maximiseChatHead();
                }else if(bCast.equals(MyActivity.MINIMIZED)){
                    minimiseChatHead();
                }
            }
        };
    }

    void setChatHeadListener(ChatHeadListener chatHeadListener) {
        ChatHead.chatHeadListener = chatHeadListener;
    }

    public static State getState() {
        return state;
    }

    private enum State {
        MAXIMIZED, MINIMIZED, LOADING
    }
}
公共类聊天室{
私有最终视图maxView、minView;
私人最终语境;
非公开最后通知;
私人最终通知ID;
专用静态聊天头;
私有静态;
私人最终ChatHeadOrientationListener ChatHeadOrientationListener;
私人浮动比率=0;
私有浮动oldWidth=0;
私有浮动oldX=0;
私有布尔更改=false;
专用静态ChatHeadListener ChatHeadListener;
私有静态最终字符串TAG=“chatfead”;
私人碎片交易;
/**
*
*@返回通过
*{@link#createInstance}方法。
*/
公共视图getMaxView(){
返回chatwead.maxView;
}
/**
*
*创建浮动窗口的单例
*
*@param context应用程序上下文
*@param maxView maxView视图,单击它将打开主体
*@param notificationId通知的通知ID
*@param notification为前台服务显示的通知
*@param chatHeadOrientionListener{@link chatHeadOrientionListener}接口
*具有在方向更改时调用的回调。
*@返回浮动窗口
*
*/
公共静态同步聊天头createInstance(上下文上下文、视图maxView、视图minView、,
int notificationId,通知,
ChatHeadOrientationListener
chatHeadOrientationListener){
if(chatHead==null){
chatHead=新chatHead(上下文、maxView、minView、notificationId、通知、,
chatHeadOrientationListener);
}
返回聊天室;
}
/**
*
*创建浮动窗口的单例
*
*@param context应用程序上下文
*@param maxView maxView视图,单击它将打开主体
*@param notificationId通知的通知ID
*@param notification为前台服务显示的通知
*@返回浮动窗口
*
*/
公共静态同步聊天头createInstance(上下文上下文、视图maxView、视图minView、,
int notificationId,通知){
if(chatHead==null){
chatHead=新chatHead(上下文、maxView、minView、notificationId、,
通知,新建ChatHeadOrientionListener(){
@凌驾
更改方向前的公共无效(聊天室管理员聊天室管理员){
Log.d(标签“beforeOrientationChange”);
}
@凌驾
方向更改后的公共无效(聊天室聊天室聊天室){
Log.d(标签“afterOrientationChange”);
}
});
}
返回聊天室;
}
/**
*@返回通过创建的浮动窗口的相同实例
*{@link#createInstance}。不要在createInstance之前调用此方法
*/
静态同步ChatHead getInstance(){
if(chatHead==null){
抛出新的NullPointerException(“ChatHead未初始化!”+
首先调用createInstance方法,然后访问+
“任何其他类中的ChatHead调用getInstance()”;
}
返回聊天室;
}
私有聊天头(上下文上下文、视图maxView、视图minView、int notificationId、,
通知通知,ChatHeadOrientationListener ChatHeadOrientationListener){
this.maxView=maxView;
this.minView=minView;
this.context=上下文;
this.notification=通知;
this.notificationId=notificationId;
this.chatHeadOrientionListener=chatHeadOrientionListener;
}
/**
*启动服务并将其添加到屏幕
*/
公共无效startService(){
意向意向=新意向(上下文,ChatHeadService.class);
上下文。startService(意图);
}
/**
*停止服务并将其从屏幕上删除
*/
公共服务{
意向意向=新意向(上下文,ChatHeadService.class);
上下文。停止服务(意图);
}
/**
*
*用于创建通知的助手方法。
*
*@param上下文
*@param contentTitle
*@param contentText
*@param通知图标
*@param contentIntent
*@服务的返回通知
*/
公共静态通知createNotification(上下文,
字符串contentTitle、字符串contentText、,
int notificationIcon,PendingEvent contentIntent){
返回n
<service
    android:name="com.maavratech.chatheads.ChatHead$ChatHeadService"
    android:exported="true"/>

<activity
    android:name=".MyActivity"
    android:label="@string/app_name"
    android:noHistory="true"
    android:launchMode="singleTask"
    android:excludeFromRecents="true"
    android:theme="@style/ThemeChatHead"/>