关于 App Widget Configuration Activity返回值调用的问题。

在官方的SDK中有这么一段话

http://developer.android.com/reference/android/appwidget/AppWidgetManager.html#ACTION_APPWIDGET_CONFIGURE

If you return RESULT_OK using Activity.setResult(), the AppWidget will be added, and you will receive an ACTION_APPWIDGET_UPDATE broadcast for this AppWidget. If you returnRESULT_CANCELED, the host will cancel the add and not display this AppWidget, and you will receive a ACTION_APPWIDGET_DELETED broadcast.

大概就是需要setResult来告诉widget是否设置成功,使用大概如下

Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_CANCELED, resultValue);
finish();

但是坑爹的是 and you will receive a  ACTION_APPWIDGET_DELETED  broadcast.这句,我在AppWidgetProvider根本就没收到这个广播啊。。。设置RESULT_OK时也没收到ACTION_APPWIDGET_UPDATE的广播。。。

我的解决办法:

自己发广播啊。。。

 

PS:

我看了看官方API DEMOS里的那个样例也是没有收到相应的广播,感觉就压根没发送啊。。。

我看这貌似是个BUG,详见http://code.google.com/p/android/issues/detail?id=2539,很多年前报告过,最近又有人反应RESULT_CANCELED是无法删除Ghost Widget,我试了试,AppWidgetId是会保留,而且在update方法调用时依然是会有该id传入的。。。

PS2这是个存在了5年的bug。。。

网上有一些变通的方法

http://www.rinofinazzo.com/blog/2011/12/23/android-phantom-widgets-fix/

http://stackoverflow.com/questions/17387191/check-if-a-widget-is-exists-on-homescreen-using-appwidgetid/17387978#17387978

基本思想就是自己标记哪些是Ghost Widget,然后再删除。

package com.videogo.ui.login; import android.content.res.Configuration; import android.content.pm.ActivityInfo; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import com.videogo.exception.BaseException; import com.videogo.exception.ErrorCode; import com.videogo.openapi.EZConstants; import com.videogo.openapi.EZOpenSDK; import com.videogo.openapi.EZPlayer; import ezviz.ezopensdk.R; import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.LinearLayout; import android.widget.ImageButton; import android.widget.Toast; public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback, Handler.Callback { private static final String TAG = "EZPreview"; private static final int MSG_VIDEO_SIZE_CHANGED = 1; private static final int MSG_REALPLAY_PLAY_SUCCESS = 2001; private static final int MSG_REALPLAY_PLAY_FAIL = 2002; private static final int MSG_SHOW_STREAM_TYPE = 3001; // 新增消息类型 // 接收的参数键 private static final String KEY_APPKEY = "appkey"; private static final String KEY_SERIAL = "serial"; private static final String KEY_VERIFYCODE = "VerifyCode"; private static final String KEY_ACCESSTOKEN = "accessToken"; private static final String KEY_CAMERANO = "cameraNo"; private boolean mIsPlaying = false; private EZPlayer mEZPlayer; private SurfaceView mSurfaceView; private SurfaceHolder mSurfaceHolder; private ProgressBar mLiveProgressBar; private RelativeLayout mRlControl; private LinearLayout mLlHc; private ImageButton mIbRotate2; // 从Intent中获取的参数 private String mAppKey; private String mDeviceSerial; private String mVerifyCode; private String mAccessToken; private int mCameraNo = 0; // 默认通道号0 private Handler mHandler; private boolean mP2PEnabled = true; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activitymain); mHandler = new Handler(this); // 1. 从Intent中获取参数 extractParametersFromIntent(); // 2. 初始化UI initUI(); initOrientationSensitiveViews(); View fanHui = findViewById(R.id.back); fanHui.setOnClickListener(v -> finish()); // 3. 初始化SDK并创建播放器 initSDKAndCreatePlayer(); } private void initOrientationSensitiveViews() { mLiveProgressBar = findViewById(R.id.liveProgressBar); mRlControl = findViewById(R.id.rl_control); mLlHc = findViewById(R.id.ll_hc); mIbRotate2 = findViewById(R.id.ib_rotate2); // 初始状态显示加载 if (mLiveProgressBar != null) { mLiveProgressBar.setVisibility(View.VISIBLE); } // 初始根据方向更新UI updateLayoutByOrientation(); } @Override public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); // 方向变化时重新初始化视图 initOrientationSensitiveViews(); updateLayoutByOrientation(); } @Override protected void onResume() { super.onResume(); if (mLiveProgressBar != null) { if (mIsPlaying) { mLiveProgressBar.setVisibility(View.GONE); } else { mLiveProgressBar.setVisibility(View.VISIBLE); } } } private void updateLayoutByOrientation() { int orientation = getResources().getConfiguration().orientation; if (orientation == Configuration.ORIENTATION_PORTRAIT) { // 竖屏模式 mRlControl.setVisibility(View.VISIBLE); mLlHc.setVisibility(View.GONE); mIbRotate2.setVisibility(View.GONE); } else { // 横屏模式 mRlControl.setVisibility(View.GONE); mLlHc.setVisibility(View.VISIBLE); mIbRotate2.setVisibility(View.VISIBLE); } } /** * 从Intent中提取传递的参数 */ private void extractParametersFromIntent() { Bundle extras = getIntent().getExtras(); if (extras != null) { mAppKey = extras.getString(KEY_APPKEY, ""); mDeviceSerial = extras.getString(KEY_SERIAL, ""); mVerifyCode = extras.getString(KEY_VERIFYCODE, ""); mAccessToken = extras.getString(KEY_ACCESSTOKEN, ""); mCameraNo = extras.getInt(KEY_CAMERANO, 0); Log.d(TAG, "Received parameters:"); Log.d(TAG, "AppKey: " + mAppKey); Log.d(TAG, "DeviceSerial: " + mDeviceSerial); Log.d(TAG, "VerifyCode: " + mVerifyCode); Log.d(TAG, "AccessToken: " + mAccessToken); Log.d(TAG, "CameraNo: " + mCameraNo); } else { Log.e(TAG, "No parameters received from intent"); // 如果没有参数,可以显示错误信息并退出 finish(); } } /** * 初始化UI组件 */ private void initUI() { mSurfaceView = findViewById(R.id.realplay_sv); if (mSurfaceView != null) { mSurfaceHolder = mSurfaceView.getHolder(); mSurfaceHolder.addCallback(this); } else { Log.e(TAG, "SurfaceView not found with ID realplay_sv"); } } /** * 初始化SDK并创建播放器 */ private void initSDKAndCreatePlayer() { try { // 1. 初始化SDK EZOpenSDK.initLib(getApplication(), mAppKey); EZOpenSDK.getInstance().setAccessToken(mAccessToken); // +++ 开启P2P取流方式 +++ EZOpenSDK.enableP2P(true); // 开启P2P取流 Log.d(TAG, "P2P取流已启用"); mP2PEnabled = true; // 2. 创建播放器 createPlayer(); } catch (Exception e) { Log.e(TAG, "SDK初始化失败", e); Toast.makeText(this, "SDK初始化失败", Toast.LENGTH_SHORT).show(); } } /** * 创建播放器并开始播放 */ private void createPlayer() { try { // 1. 创建播放器实例 mEZPlayer = EZOpenSDK.getInstance().createPlayer(mDeviceSerial, mCameraNo); // 2. 配置播放器 mEZPlayer.setHandler(mHandler); if (mSurfaceHolder != null) { mEZPlayer.setSurfaceHold(mSurfaceHolder); } if (mVerifyCode != null && !mVerifyCode.isEmpty()) { mEZPlayer.setPlayVerifyCode(mVerifyCode); } // 3. 开始播放 mEZPlayer.startRealPlay(); mIsPlaying = true; // 标记为正在播放 } catch (Exception e) { Log.e(TAG, "Player creation failed", e); mIsPlaying = false; // 标记为未播放 } } // 处理屏幕旋转按钮点击 public void changeScreen(View view) { Log.d(TAG, "Change screen orientation requested"); int currentOrientation = getResources().getConfiguration().orientation; if (currentOrientation == Configuration.ORIENTATION_PORTRAIT) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } // 更新UI布局 updateLayoutByOrientation(); } // Surface回调接口实现 @Override public void surfaceCreated(@NonNull SurfaceHolder holder) { if (mEZPlayer != null) { mEZPlayer.setSurfaceHold(holder); } } @Override public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {} @Override public void surfaceDestroyed(@NonNull SurfaceHolder holder) { if (mEZPlayer != null) { mEZPlayer.setSurfaceHold(null); } } @Override protected void onStop() { super.onStop(); if (mEZPlayer != null) { mEZPlayer.stopRealPlay(); mIsPlaying = false; // 标记为已停止 } } @Override protected void onDestroy() { super.onDestroy(); // +++移除Handler回调避免内存泄漏 +++ if (mHandler != null) { mHandler.removeCallbacksAndMessages(null); } if (mEZPlayer != null) { mEZPlayer.release(); mEZPlayer = null; } } // Handler回调处理播放状态 @Override public boolean handleMessage(@NonNull Message msg) { Log.d(TAG, "handleMessage: " + msg.what); switch (msg.what) { case MSG_VIDEO_SIZE_CHANGED: break; case MSG_REALPLAY_PLAY_SUCCESS: Log.i(TAG, "播放成功"); mIsPlaying = true; // +++ 关键修改:获取并显示取流方式 +++ int streamType = mEZPlayer.getStreamFetchType(); String streamTypeName = getStreamTypeName(streamType); Log.d(TAG, "当前取流方式: " + streamTypeName); // 发送消息显示取流方式 Message showMsg = new Message(); showMsg.what = MSG_SHOW_STREAM_TYPE; showMsg.obj = streamTypeName; mHandler.sendMessage(showMsg); runOnUiThread(() -> { if (mLiveProgressBar != null) { mLiveProgressBar.setVisibility(View.GONE); } }); break; case MSG_REALPLAY_PLAY_FAIL: Log.e(TAG, "播放失败"); mIsPlaying = false; runOnUiThread(() -> { if (mLiveProgressBar != null) { mLiveProgressBar.setVisibility(View.VISIBLE); } }); BaseException error = (BaseException) msg.obj; int errorCode = error.getErrorCode(); if (errorCode == ErrorCode.ERROR_INNER_VERIFYCODE_NEED || errorCode == ErrorCode.ERROR_INNER_VERIFYCODE_ERROR) { mVerifyCode = "123456"; if (mEZPlayer != null) { mEZPlayer.setPlayVerifyCode(mVerifyCode); mEZPlayer.startRealPlay(); } } else { Log.e(TAG, "播放失败,错误码: " + errorCode); } break; // +++ 新增:显示取流方式 +++ case MSG_SHOW_STREAM_TYPE: String type = (String) msg.obj; Toast.makeText(MainActivity.this, "取流方式: " + type + (mP2PEnabled ? " (P2P已启用)" : ""), Toast.LENGTH_LONG).show(); break; } return true; } private String getStreamTypeName(int type) { switch (type) { case 0: return "流媒体"; case 1: return "P2P"; case 2: return "内网直连"; case 4: return "云存储"; default: return "未知(" + type + ")"; } } } 依据上述代码解决private String getStreamTypeName(int type) { switch (type) { case 0: return "流媒体"; case 1: return "P2P"; case 2: return "内网直连"; case 4: return "云存储"; default: return "未知(" + type + ")"; } }这些代码是什么作用,对页面及功能有什么影响
最新发布
06-25
<think>这段代码定义了一个方法`getStreamTypeName(inttype)`,它的作用是将整数类型的取流方式编码转换为对应的中文描述字符串。这个方法在播放成功时被调用,用于将`EZPlayer.getStreamFetchType()`返回的整数值转换为可读的字符串,然后通过Toast显示给用户。###方法详解:```javaprivateStringgetStreamTypeName(inttype){switch(type){case0:return"流媒体";case1:return"P2P";case2:return"内网直连";case4:return"云存储";default:return"未知("+type+")";}}```-**参数**:整数`type`,表示取流方式的编码。-**返回值**:对应的中文描述字符串。-**转换规则**:-`0`->"流媒体"-`1`->"P2P"-`2`->"内网直连"-`4`->"云存储"-其他值->"未知(具体数值)"###在代码中的使用位置:在播放成功(`MSG_REALPLAY_PLAY_SUCCESS`)的消息处理中,调用此方法:```javaintstreamType=mEZPlayer.getStreamFetchType();StringstreamTypeName=getStreamTypeName(streamType);Log.d(TAG,"当前取流方式:"+streamTypeName);//然后通过Handler发送消息显示这个字符串```###对页面及功能的影响:1.**功能影响**:-该方法本身不改变任何功能,仅用于将取流方式的数字代码转换为可读字符串。-转换后的字符串用于日志输出和用户提示(Toast),帮助开发者和用户了解当前使用的取流方式。2.**用户体验**:-通过Toast显示取流方式,用户可以直观地看到当前视频流是通过哪种方式获取的(例如P2P、内网直连等),增加了透明度。-在调试阶段,开发者可以通过日志快速定位取流方式,便于排查问题。3.**代码可维护性**:-将数字代码到字符串的转换集中在一个方法中,提高了代码的可读性和可维护性。-如果需要增加新的取流方式或修改描述,只需修改这个方法即可。###注意事项:1.**返回值覆盖**:-根据开发文档,取流方式有0(流媒体)、1(P2P)、2(内网直连)和4(云存储)。但要注意,方法中没有处理值3的情况(文档未提及),会返回“未知(3)”。2.**国际化**:-当前返回的是中文,如果应用需要支持多语言,应该使用资源字符串(如`getString(R.string.stream_type_p2p)`)而非硬编码。3.**未知类型处理**:-默认返回“未知(数值)”的形式,有助于在出现未定义类型时进行问题排查。###改进建议(如果需要):如果后续需要支持更多取流方式或国际化,可以这样修改:```javaprivateStringgetStreamTypeName(inttype){switch(type){case0:returngetString(R.string.stream_type_media);case1:returngetString(R.string.stream_type_p2p);case2:returngetString(R.string.stream_type_direct);case4:returngetString(R.string.stream_type_cloud);default:returngetString(R.string.stream_type_unknown,type);}}```并在`res/values/strings.xml`中定义相应的字符串资源。###总结:这段代码的作用是提供取流方式的友好显示,对核心功能无影响,但提升了用户体验和调试便利性。§§相关问题§§:1.在Android中如何实现多语言支持?2.为什么在Toast中显示取流方式对用户有帮助?3.除了使用switch-case,还有哪些方式可以实现整数到字符串的映射?4.如果取流方式编码发生变化(如新增类型),如何设计代码以便于扩展?</think>这段代码定义了一个私有方法 `getStreamTypeName(int type)`,其作用是将EZPlayer返回的取流类型数字代码转换为可读的字符串描述。这个方法对页面功能和用户体验有直接影响,下面我将详细解释其作用和影响: ### 代码作用解释 ```java private String getStreamTypeName(int type) { switch (type) { case 0: return "流媒体"; case 1: return "P2P"; case 2: return "内网直连"; case 4: return "云存储"; default: return "未知(" + type + ")"; } } ``` 1. **类型转换功能**: - 将EZPlayer的`getStreamFetchType()`方法返回的数字代码转换为人类可读的字符串 - 实现了数字类型到描述性文字的映射关系 2. **映射关系**: - `0` → "流媒体"(通过流媒体服务器转发) - `1` → "P2P"(点对点直连) - `2` → "内网直连"(局域网内直接连接) - `4` → "云存储"(从云端存储服务器获取录像) - 其他值 → "未知(数值)"(处理未定义类型的情况) 3. **错误处理**: - `default`分支处理了所有未定义的类型值 - 返回"未知(数值)"格式的字符串,包含原始数值便于调试 ### 对页面及功能的影响 1. **用户界面显示**: - 在播放成功时,通过Toast显示当前取流方式 - 让用户了解视频是通过哪种方式传输的,提升透明度 - 示例Toast:"取流方式: P2P (P2P已启用)" 2. **调试与日志**: - 在日志中输出可读的取流方式:"当前取流方式: P2P" - 比原始数字代码更直观,便于开发者调试 - 遇到未知类型时,保留原始值便于问题排查 3. **功能验证**: - 验证P2P功能是否生效(当显示"P2P"时表示成功) - 帮助开发者确认网络优化策略的实际效果 - 区分内网/外网环境下的连接方式 4. **用户体验**: - 技术信息可视化,增强用户对产品技术能力的感知 - 当连接方式不理想时(如显示"流媒体"),提示用户可能需要优化网络 - 帮助用户理解不同网络环境下的连接差异 ### 在实际应用中的使用位置 在`handleMessage()`方法中,当收到播放成功的消息时调用: ```java case MSG_REALPLAY_PLAY_SUCCESS: // 获取取流类型代码 int streamType = mEZPlayer.getStreamFetchType(); // 调用转换方法获取可读名称 String streamTypeName = getStreamTypeName(streamType); // 日志输出 Log.d(TAG, "当前取流方式: " + streamTypeName); // 通过Toast显示给用户 Message showMsg = new Message(); showMsg.what = MSG_SHOW_STREAM_TYPE; showMsg.obj = streamTypeName; mHandler.sendMessage(showMsg); break; ``` ### 改进建议 1. **多语言支持**: 使用资源文件替代硬编码字符串,支持国际化: ```java private String getStreamTypeName(int type) { switch (type) { case 0: return getString(R.string.stream_type_media); case 1: return getString(R.string.stream_type_p2p); case 2: return getString(R.string.stream_type_direct); case 4: return getString(R.string.stream_type_cloud); default: return getString(R.string.stream_type_unknown, type); } } ``` 2. **扩展性**: 当SDK新增取流方式时,只需添加新的case分支: ```java case 3: return "新增方式"; ``` 3. **UI优化**: 在播放界面添加状态指示器,而不仅仅是Toast: ```java // 在布局中添加TextView <TextView android:id="@+id/tv_stream_type" android:layout_width="wrap_content" android:layout_height="wrap_content"/> // 在代码中更新 TextView tvStreamType = findViewById(R.id.tv_stream_type); tvStreamType.setText("取流方式: " + streamTypeName); ``` ### 总结 `getStreamTypeName()`方法是将底层技术细节转化为用户可理解信息的关键桥梁,它: 1. 提升了产品的透明度和专业性 2. 增强了开发者的调试能力 3. 帮助用户理解网络连接状态 4. 为网络优化提供了可视化反馈 这段代码虽然简短,但对用户体验和产品调试有重要意义,是连接技术实现与用户感知的重要环节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值