Android MediaPlayer+SurfaceView播放视频 (异常处理)

来源:互联网 发布:软件项目质量承诺书 编辑:程序博客网 时间:2024/06/05 06:21

MediaPlayer,顾名思义是用于媒体文件播放的组件。Android中MediaPlayer通常与SurfaceView一起使用,当然也可以和其他控件诸如TextureView、SurfaceTexture等可以取得holder,用于MediaPlayer.setDisplay的控件一起使用。 
对于现在的移动设备来说,媒体播放时一个非常重要的功能,所以掌握MediaPlayer对于android程序员来说,也是一个基本要求了。由于媒体播放是一个比较复杂的事情,涉及到媒体资源的加载、解码等耗时耗资源的操作,所以MediaPlayer的使用相对其他组件变得复杂了许多。 
掌握MediaPlayer需要先掌握MediaPlayer的工作过程和它的一些重要的方法,在Android Developer官网上可以搜到MediaPlayer详细的讲解。

MediaPlayer状态机

在官网上可以看到一张关于MediaPayer状态机的图,直观的阐述了MediaPlayer的工作过程,以及它的一些重要的方法的使用时机。如下: 
这里写图片描述 
从上图中,可以捋出MediaPlayer的一个最简单的使用流程:

  1. 新建一个MediaPlayer: mPlayer=new MediaPlayer();通常在新建一个MediaPlayer实体后,会对给它增加需要的监听事件,MediaPlayer的监听事件有:

    • MediaPlayer.OnPreparedListener:MediaPlayer进入准备完成的状态触发,表示媒体可以开始播放了。
    • MediaPlayer.OnSeekCompleteListener:调用MediaPlayer的seekTo方法后,MediaPlayer会跳转到媒体指定的位置,当跳转完成时触发。需要注意的时,seekTo并不能精确的挑战,它的跳转点必须是媒体资源的关键帧。
    • MediaPlayer.OnBufferingUpdateListener:网络上的媒体资源缓存进度更新的时候会触发。
    • MediaPlayer.OnCompletionListener:媒体播放完毕时会触发。但是当OnErrorLister返回false,或者MediaPlayer没有设置OnErrorListener时,这个监听也会被触发。
    • MediaPlayer.OnVideoSizeChangedListener:视频宽高发生改变的时候会触发。当所设置的媒体资源没有视频图像、MediaPlayer没有设置展示的holder或者视频大小还没有被测量出来时,获取宽高得到的都是0.
    • MediaPlayer.OnErrorListener:MediaPlayer出错时会触发,无论是播放过程中出错,还是准备过程中出错,都会触发。
  2. 将需要播放的资源路径交给MediaPlayer实体:mPlayer.setDataSource(source);

  3. 让MediaPlayer去获取解析资源,调用prepare()或者prepareAsync()方法,前一个是同步方法,后一个是异步方法,通常我们用的比较多的是后者:mPlayer.prepareAsync();
  4. 进入准备完成状态后,调用start()方法开始播放,如果是调用prepare()方法准备,在prepare()方法后,可以直接开始播放。如果是调用prepareAsync()方法准备,需要在OnPreparedListener()监听中开始播放:mPlayer.start(); 
    这是一个最简单的播放流程,然而我们的需求绝不可能这么简单!通过以上流程我们会遇到很多问题。

MediaPlayer使用常见问题

按照上面所说的流程来操作,我们会发现还有很多问题需要处理,比如说视频播放有声音没图像,切入后台后声音还在播放等等问题。综合一下,我们在安装上述流程走会有哪些问题以及我们解决一些问题后,还可能遇到哪些问题:

  • 视频播放有声音没图像。
  • 视频图像变形。
  • 切入后台后声音还在继续播放。
  • 切入后台再切回来,视频黑屏。
  • 暂停后切入后台,再切回来,并保持暂停状态会黑屏,seekTo也没有用。
  • 播放时会有一小段时间的黑屏。
  • 多个SurfaceView用来播放视频,滑动切换时会有上个视频的残影。

等等一些其他更多问题。最为典型的应该就是上述这些问题了。这些问题,仔细看看官网上对于MediaPlayer的讲解后,基本都不会是问题。恩,最后一个问题除外。相对MediaPlayer的状态机来说,MediaPlayer的各个方法的有效状态和无效状态为我们在使用MediaPlayer的具体方法时,提供了更好的指南。

Valid and invalid states

感觉用有效状态和无效状态来翻译不太合适,干脆直接就用官方上面所说的Valid and invalid states吧。它指出了MediaPlayer中常用公有方法在那些状态下可以使用,在那些状态下不可以使用。 
我们可以将所有的方法分为三类。 
- 在任何状态下都可以使用的。比如设置监听,以及其他MediaPlayer中与资源无关的方法。需要特别注意的是setDisplay和setSurface两个方法。 
- 在MediaPlayer状态机中除Error状态都可以使用的。比如获取视频宽高、获取当前位置等。 
- 对状态有诸多限制,需要严格遵循状态机流程的方法。 比如start、pause、stop等等方法。 
具体的在MediaPlayer官方说明中有对应的表。

常见问题讨论

针对上面提到的问题,通过MediaPlayer的状态机和它的常用方法的可用状态来进行讨论,我们就能找到相应的原因,因为代码是不会欺骗的。 
1. 有声音没有图像 
视频播放有声音没图像也许是在使用MediaPlayer最容易出现的问题,几乎所有使用MediaPlayer的新手都会遇到。视频播放的图像呈现需要一个载体,需要利用MediaPlayer.setDisplay设置一个展示视频画面的SurfaceHolder,最终视频的每一帧图像是要绘制在Surface上面的。通常,设置给MediaPlayer的SurfaceHolder未被创建,视频播放就注定没有图像。 
* 比如你先调用了setDisplay,但是这个时候holder是没有被创建的。视频就没有图像了。 
* 或者你在setDisplay的时候holder确保了holder是被创建了,但是当因为一些原因holder被销毁了,视频也就没有图像了。 
* 再者,你没有给展示视频的view设置合适的大小,比如都设置wrap_content,或者都设置0,也会导致SurfaceHolder不能被创建,视频也就没有图像了。 
2. 视频图像变形 
Surface展示视频图像的时候,是不会去主动保证和呈现出来的图像和原始图像的宽高比例是一致的,所以我们需要自己去设置展示视频的View的宽高,以保证视频图像展示出来的时候不会变形。我认为比较合适的做法就是利用FrameLayout嵌套一个SurfaceView或者其他拥有Surface的View来作为视频图像播放的载体View,然后再OnVideoSizeChangeListener的监听回调中,对载体View的大小做更改。 
3. 切入后台后声音还在继续播放 
这个问题只需要在onPause中暂停播放即可 
4. 切入后台再切回来,视频黑屏 
诸如此类的黑屏问题,多是因为surfaceholder被销毁了,再切回来时,需要重新给MediaPlayer设置holder。 
5. 播放时会有一小段时间的黑屏 
视频准备完成后,调用play进行播放视频,承载视频播放的View会是黑屏状态,我们只需要在播放前,给对应的Surface绘制一张图即可。 
6. 多个SurfaceView用来播放视频,滑动切换时会有上个视频的残影 
当视频切换出界面,设置surfaceView的visiable状态为Gone,界面切回来时再设置为visiable即可。

MediaPlayer使用示例

将MediaPlayer的控制单独写到一个类中:

public class MPlayer implements IMPlayer,MediaPlayer.OnBufferingUpdateListener,        MediaPlayer.OnCompletionListener,MediaPlayer.OnVideoSizeChangedListener,        MediaPlayer.OnPreparedListener,MediaPlayer.OnSeekCompleteListener,        MediaPlayer.OnErrorListener,SurfaceHolder.Callback{    private MediaPlayer player;    private String source;    private IMDisplay display;    private boolean isVideoSizeMeasured=false;  //视频宽高是否已获取,且不为0    private boolean isMediaPrepared=false;      //视频资源是否准备完成    private boolean isSurfaceCreated=false;     //Surface是否被创建    private boolean isUserWantToPlay=false;     //使用者是否打算播放    private boolean isResumed=false;            //是否在Resume状态    private boolean mIsCrop=false;    private IMPlayListener mPlayListener;    private int currentVideoWidth;              //当前视频宽度    private int currentVideoHeight;             //当前视频高度    private void createPlayerIfNeed(){        if(null==player){            player=new MediaPlayer();            player.setScreenOnWhilePlaying(true);            player.setOnBufferingUpdateListener(this);            player.setOnVideoSizeChangedListener(this);            player.setOnCompletionListener(this);            player.setOnPreparedListener(this);            player.setOnSeekCompleteListener(this);            player.setOnErrorListener(this);        }    }    private void playStart(){        if(isVideoSizeMeasured&&isMediaPrepared&&isSurfaceCreated&&isUserWantToPlay&&isResumed){            player.setDisplay(display.getHolder());            player.start();            log("视频开始播放");            display.onStart(this);            if(mPlayListener!=null){                mPlayListener.onStart(this);            }        }    }    private void playPause(){        if(player!=null&&player.isPlaying()){            player.pause();            display.onPause(this);            if(mPlayListener!=null){                mPlayListener.onPause(this);            }        }    }    private boolean checkPlay(){        if(source==null|| source.length()==0){            return false;        }        return true;    }    public void setPlayListener(IMPlayListener listener){        this.mPlayListener=listener;    }    /**     * 设置是否裁剪视频,若裁剪,则视频按照DisplayView的父布局大小显示。     * 若不裁剪,视频居中于DisplayView的父布局显示     * @param isCrop 是否裁剪视频     */    public void setCrop(boolean isCrop){        this.mIsCrop=isCrop;        if(display!=null&&currentVideoWidth>0&&currentVideoHeight>0){            tryResetSurfaceSize(display.getDisplayView(),currentVideoWidth,currentVideoHeight);        }    }    public boolean isCrop(){        return mIsCrop;    }    /**     * 视频状态     * @return 视频是否正在播放     */    public boolean isPlaying(){        return player!=null&&player.isPlaying();    }    //根据设置和视频尺寸,调整视频播放区域的大小    private void tryResetSurfaceSize(final View view, int videoWidth, int videoHeight){        ViewGroup parent= (ViewGroup) view.getParent();        int width=parent.getWidth();        int height=parent.getHeight();        if(width>0&&height>0){            final FrameLayout.LayoutParams params= (FrameLayout.LayoutParams) view.getLayoutParams();            if(mIsCrop){                float scaleVideo=videoWidth/(float)videoHeight;                float scaleSurface=width/(float)height;                if(scaleVideo<scaleSurface){                    params.width=width;                    params.height= (int) (width/scaleVideo);                    params.setMargins(0,(height-params.height)/2,0,(height-params.height)/2);                }else{                    params.height=height;                    params.width= (int) (height*scaleVideo);                    params.setMargins((width-params.width)/2,0,(width-params.width)/2,0);                }            }else{                if(videoWidth>width||videoHeight>height){                    float scaleVideo=videoWidth/(float)videoHeight;                    float scaleSurface=width/height;                    if(scaleVideo>scaleSurface){                        params.width=width;                        params.height= (int) (width/scaleVideo);                        params.setMargins(0,(height-params.height)/2,0,(height-params.height)/2);                    }else{                        params.height=height;                        params.width= (int) (height*scaleVideo);                        params.setMargins((width-params.width)/2,0,(width-params.width)/2,0);                    }                }            }            view.setLayoutParams(params);        }    }    @Override    public void setSource(String url) throws MPlayerException {        this.source=url;        createPlayerIfNeed();        isMediaPrepared=false;        isVideoSizeMeasured=false;        currentVideoWidth=0;        currentVideoHeight=0;        player.reset();        try {            player.setDataSource(url);            player.prepareAsync();            log("异步准备视频");        } catch (IOException e) {            throw new MPlayerException("set source error",e);        }    }    @Override    public void setDisplay(IMDisplay display) {        if(this.display!=null&&this.display.getHolder()!=null){            this.display.getHolder().removeCallback(this);        }        this.display=display;        this.display.getHolder().addCallback(this);    }    @Override    public void play() throws MPlayerException {        if(!checkPlay()){            throw new MPlayerException("Please setSource");        }        createPlayerIfNeed();        isUserWantToPlay=true;        playStart();    }    @Override    public void pause() {        isUserWantToPlay=false;        playPause();    }    @Override    public void onPause() {        isResumed=false;        playPause();    }    @Override    public void onResume() {        isResumed=true;        playStart();    }    @Override    public void onDestroy() {        if(player!=null){            player.release();        }    }    @Override    public void onBufferingUpdate(MediaPlayer mp, int percent) {    }    @Override    public void onCompletion(MediaPlayer mp) {        display.onComplete(this);        if(mPlayListener!=null){            mPlayListener.onComplete(this);        }    }    @Override    public void onPrepared(MediaPlayer mp) {        log("视频准备完成");        isMediaPrepared=true;        playStart();    }    @Override    public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {        log("视频大小被改变->"+width+"/"+height);        if(width>0&&height>0){            this.currentVideoWidth=width;            this.currentVideoHeight=height;            tryResetSurfaceSize(display.getDisplayView(),width,height);            isVideoSizeMeasured=true;            playStart();        }    }    @Override    public void onSeekComplete(MediaPlayer mp) {    }    @Override    public boolean onError(MediaPlayer mp, int what, int extra) {        return false;    }    @Override    public void surfaceCreated(SurfaceHolder holder) {        if(display!=null&&holder==display.getHolder()){            isSurfaceCreated=true;            //此举保证以下操作下,不会黑屏。(或许还是会有手机黑屏)            //暂停,然后切入后台,再切到前台,保持暂停状态            if(player!=null){                player.setDisplay(holder);                //不加此句360f4不会黑屏、小米note1会黑屏,其他机型未测                player.seekTo(player.getCurrentPosition());            }            log("surface被创建");            playStart();        }    }    @Override    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {        log("surface大小改变");    }    @Override    public void surfaceDestroyed(SurfaceHolder holder) {        if(display!=null&&holder==display.getHolder()){            log("surface被销毁");            isSurfaceCreated=false;        }    }    private void log(String content){        Log.e("MPlayer",content);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268

然后通过MPlayer即可更为简单方便的播放视频:

public class PlayerActivity extends Activity {    private EditText mEditAddress;    private SurfaceView mPlayerView;    private MPlayer player;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_player);        initView();        initPlayer();    }    private void initView(){        mEditAddress= (EditText) findViewById(R.id.mEditAddress);        mPlayerView= (SurfaceView) findViewById(R.id.mPlayerView);    }    private void initPlayer(){        player=new MPlayer();        player.setDisplay(new MinimalDisplay(mPlayerView));    }    @Override    protected void onResume() {        super.onResume();        player.onResume();    }    @Override    protected void onPause() {        super.onPause();        player.onPause();    }    @Override    protected void onDestroy() {        super.onDestroy();        player.onDestroy();    }    public void onClick(View view){        switch (view.getId()){            case R.id.mPlay:                String mUrl=mEditAddress.getText().toString();                if(mUrl.length()>0){                    Log.e("wuwang","播放->"+mUrl);                    try {                        player.setSource(mUrl);                        player.play();                    } catch (MPlayerException e) {                        e.printStackTrace();                    }                }                break;            case R.id.mPlayerView:                if(player.isPlaying()){                    player.pause();                }else{                    try {                        player.play();                    } catch (MPlayerException e) {                        e.printStackTrace();                    }                }                break;            case R.id.mType:                player.setCrop(!player.isCrop());                break;        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

完整Demo地址

阅读全文
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 手机淘宝太卡怎么办 卖家不同意退货怎么办 游戏退出无响应怎么办 手机淘宝购物车打不开怎么办 淘宝店铺没有访客怎么办 淘宝店铺0流量怎么办 微信经常封号怎么办 网上拍卖堂违约怎么办 dnf4开组队制裁怎么办 红酒木塞丢了怎么办 红酒塞子进去了怎么办 淘金币即将过期怎么办 淘金币过期怎么办2018 换详情排名下降怎么办 长城宽带不用了怎么办 快递到了想退货怎么办 淘宝退货商家拒收怎么办 淘宝运费险失败怎么办 忘记购买运费险怎么办 咸鱼买家申请退款怎么办 熟猪肉有点变味怎么办 和领导意见不一致怎么办 骑手提前点送达怎么办 ubuntu安装报错怎么办 液相色谱两峰分不开怎么办 液相色谱柱老堵怎么办? 没有装usb驱动怎么办 ipad速度越来越慢怎么办 美萍管理软件打不开怎么办 小米4开机黑屏怎么办 小米电脑死机了怎么办 小米8手机死机怎么办 oppa7开不了机怎么办 oppo手机wlan打不开怎么办 三星s6进水黑屏怎么办 银行卡不支持快捷支付怎么办 路由器忘记管理员密码怎么办 云付没有推荐人怎么办 牛呗审核不通过怎么办 华硕笔记本很卡怎么办 淘宝换货没有货怎么办