使用TextUtils.isEmpty()遇到的坑

本文讨论了在Android开发中使用TextUtils.isEmpty()方法判断字符串是否为空时遇到的问题,并提供了一个更全面的判断方法,以避免遗漏特殊情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android开发中,我们经常使用TextUtils.isEmpty()来判断字符串是否为null或者空字符串,防止出现空指针异常,但是之前使用这个方法的时候,出现了一点小问题,所以记录下来,防止以后再犯。

TextUtils.isEmpty()的实现如下:

    /**
     * Returns true if the string is null or 0-length.
     * @param str the string to be examined
     * @return true if str is null or zero length
     */
    public static boolean isEmpty(CharSequence str) {
        if (str == null || str.length() == 0)
            return true;
        else
            return false;
    }

一般情况下使用是没问题的。
现在我们考虑这样一种情况:假设实体Student有一个属性name,String类型。后台在响应这个属性到手机端的时候,是这样赋值的name = xxx + “”,这样可以防止name属性为null,但是如果xxx为null,响应的name是什么样的呢?我们测试一下。
null字符串

响应过来的其实是字符串“null”,这样我们再使用TextUtils.isEmpty()进行非空判断的时候,就会出错,遗漏掉一种情况。健全的判断方法应该是:

!((TextUtils.isEmpty(name)) && ("null".equalsIgnoreCase(name)))
package com.videogo.ui.login; import android.app.AlertDialog; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.text.TextUtils; import android.util.Log; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.DatePicker; import android.widget.ImageButton; import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import com.videogo.exception.BaseException; import com.videogo.exception.ErrorCode; import com.videogo.openapi.EZPlayer; import androidx.appcompat.app.AppCompatActivity; import com.videogo.openapi.EZOpenSDK; import ezviz.ezopensdk.R; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class FanHui extends AppCompatActivity implements SurfaceHolder.Callback { private static final String TAG = "EZPlayback"; private String mAppKey; private String mDeviceSerial; private String mVerifyCode; private String mAccessToken; private int mCameraNo; private TextView mDateTextView; private int mSelectedYear, mSelectedMonth, mSelectedDay; 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 static final int MSG_PLAYBACK_PLAY_SUCCESS = 3001; private static final int MSG_PLAYBACK_PLAY_FAIL = 3002; private static final int MSG_PLAYBACK_STOP = 3003; // 更新API地址为云存储专用接口 private static final String VIDEO_BY_TIME_URL = "https://open.ys7.com/api/lapp/video/by/time"; // private static final String PLAYBACK_URL_API = "https://open.ys7.com/api/lapp/v2/live/address/get"; private ExecutorService mExecutorService; private ListView mListView; private PlaybackAdapter mAdapter; private List<VideoInfo> mVideoList = new ArrayList<>(); // 播放器相关 private EZPlayer mEZPlayer; private SurfaceView mPlaybackSurfaceView; private SurfaceHolder mSurfaceHolder; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.ez_playback_list_page); View fanHui = findViewById(R.id.back); fanHui.setOnClickListener(v -> finish()); // 创建线程池 mExecutorService = Executors.newFixedThreadPool(2); extractParametersFromIntent(); final Calendar calendar = Calendar.getInstance(); mSelectedYear = calendar.get(Calendar.YEAR); mSelectedMonth = calendar.get(Calendar.MONTH); mSelectedDay = calendar.get(Calendar.DAY_OF_MONTH); // 初始化视图 initViews(); // 设置日期显示模块 setupDatePicker(); // 使用MainActivity已初始化的SDK实例 EZOpenSDK.getInstance().setAccessToken(mAccessToken); // 默认加载当天的录像 loadVideosForSelectedDate(); } private void initViews() { // 查找ListView mListView = findViewById(R.id.listView); if (mListView == null) { Log.e(TAG, "ListView not found with ID listView"); return; } // 初始化适配器 mAdapter = new PlaybackAdapter(); mListView.setAdapter(mAdapter); // 设置点击事件 mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { VideoInfo video = mVideoList.get(position); playVideo(video); } }); // 获取播放器视图 - 使用SurfaceView mPlaybackSurfaceView = findViewById(R.id.remote_playback_wnd_sv); if (mPlaybackSurfaceView != null) { // 设置SurfaceHolder回调 mSurfaceHolder = mPlaybackSurfaceView.getHolder(); mSurfaceHolder.addCallback(this); } else { Log.e(TAG, "SurfaceView not found with ID remote_playback_wnd_sv"); } } // SurfaceHolder回调方法 @Override public void surfaceCreated(SurfaceHolder holder) { Log.d(TAG, "Surface created"); // 当Surface创建时,如果有播放器实例,设置SurfaceHolder if (mEZPlayer != null) { mEZPlayer.setSurfaceHold(holder); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.d(TAG, "Surface changed: " + width + "x" + height); } @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.d(TAG, "Surface destroyed"); // 当Surface销毁时,释放播放器资源 stopPlayback(); } private void setupDatePicker() { mDateTextView = findViewById(R.id.date_text); ImageButton datePickerButton = findViewById(R.id.date_picker_button); updateDateDisplay(); datePickerButton.setOnClickListener(v -> showDatePickerDialog()); } private void updateDateDisplay() { String formattedDate = String.format(Locale.getDefault(), "%d年%02d月%02d日", mSelectedYear, mSelectedMonth + 1, // 月份需要+1 mSelectedDay); mDateTextView.setText(formattedDate); } private void showDatePickerDialog() { final AlertDialog dlg = new AlertDialog.Builder(this, R.style.Theme_AppCompat_Dialog).create(); dlg.show(); Window window = dlg.getWindow(); window.setContentView(R.layout.datepicker_layout); // 设置对话框宽度 WindowManager.LayoutParams lp = window.getAttributes(); lp.width = WindowManager.LayoutParams.MATCH_PARENT; window.setAttributes(lp); // 获取并初始化 DatePicker DatePicker dpPicker = window.findViewById(R.id.dpPicker); // 隐藏不需要的视图 ViewGroup rootView = (ViewGroup) dpPicker.getChildAt(0); if (rootView != null) { ViewGroup childView = (ViewGroup) rootView.getChildAt(0); if (childView != null) { childView.getChildAt(2).setVisibility(View.VISIBLE); // 确保月选择器可见 childView.getChildAt(1).setVisibility(View.VISIBLE); } } dpPicker.init(mSelectedYear, mSelectedMonth, mSelectedDay, null); // 设置按钮事件 RelativeLayout yesButton = window.findViewById(R.id.YES); RelativeLayout noButton = window.findViewById(R.id.NO); yesButton.setOnClickListener(v -> { mSelectedYear = dpPicker.getYear(); mSelectedMonth = dpPicker.getMonth(); mSelectedDay = dpPicker.getDayOfMonth(); updateDateDisplay(); dlg.dismiss(); // 加载新选择的日期的录像 loadVideosForSelectedDate(); }); noButton.setOnClickListener(v -> dlg.dismiss()); } private void extractParametersFromIntent() { Bundle extras = getIntent().getExtras(); if (extras != null) { mAppKey = extras.getString(KEY_APPKEY, ""); mDeviceSerial = extras.getString(KEY_SERIAL, "").toUpperCase(); mVerifyCode = extras.getString(KEY_VERIFYCODE, ""); mAccessToken = extras.getString(KEY_ACCESSTOKEN, ""); mCameraNo = extras.getInt(KEY_CAMERANO, 1); // 默认为通道1 Log.d(TAG, "Received parameters:"); Log.d(TAG, "AppKey: " + mAppKey); Log.d(TAG, "DeviceSerial (Upper): " + 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"); } } private void loadVideosForSelectedDate() { if (TextUtils.isEmpty(mDeviceSerial) || TextUtils.isEmpty(mAccessToken)) { Toast.makeText(this, "设备参数不完整", Toast.LENGTH_SHORT).show(); return; } // 计算开始和结束时间戳 Calendar cal = Calendar.getInstance(); cal.set(mSelectedYear, mSelectedMonth, mSelectedDay, 0, 0, 0); long startTime = cal.getTimeInMillis(); long maxEndTime = System.currentTimeMillis(); cal.set(mSelectedYear, mSelectedMonth, mSelectedDay, 23, 59, 59); long endTime = Math.min(cal.getTimeInMillis(), maxEndTime); // 发起网络请求获取录像 fetchVideosByTime(startTime, endTime); } private void fetchVideosByTime(long startTime, long endTime) { // 检查Activity状态 if (isFinishing() || isDestroyed()) return; if (TextUtils.isEmpty(mDeviceSerial)) { runOnUiThread(() -> Toast.makeText(FanHui.this, "设备序列号为空", Toast.LENGTH_SHORT).show()); return; } // 确保序列号全大写 final String deviceSerial = mDeviceSerial.toUpperCase(); mExecutorService.execute(() -> { try { // 使用V3版本API URL url = new URL(VIDEO_BY_TIME_URL); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); conn.setDoOutput(true); conn.setConnectTimeout(10000); conn.setReadTimeout(10000); // 构建POST数据 StringBuilder postData = new StringBuilder(); postData.append("accessToken=").append(URLEncoder.encode(mAccessToken, "UTF-8")); postData.append("&deviceSerial=").append(URLEncoder.encode(deviceSerial, "UTF-8")); postData.append("&channelNo=").append(mCameraNo); postData.append("&startTime=").append(startTime); postData.append("&endTime=").append(endTime); postData.append("&recType=1"); // 1-云存储录像 OutputStream os = conn.getOutputStream(); os.write(postData.toString().getBytes("UTF-8")); os.flush(); os.close(); // 处理响应 int responseCode = conn.getResponseCode(); if (responseCode == HttpURLConnection.HTTP_OK) { InputStream is = conn.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); StringBuilder response = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { response.append(line); } reader.close(); JSONObject json = new JSONObject(response.toString()); String code = json.optString("code", "0"); if ("200".equals(code)) { JSONArray data = json.getJSONArray("data"); List<VideoInfo> videos = parseVideoData(data); // 检查Activity状态再更新UI if (!isFinishing() && !isDestroyed()) { runOnUiThread(() -> { mVideoList.clear(); mVideoList.addAll(videos); mAdapter.notifyDataSetChanged(); if (videos.isEmpty()) { Toast.makeText(FanHui.this, "该时间段没有录像", Toast.LENGTH_SHORT).show(); } }); } } else { String msg = json.optString("msg", "未知错误"); Log.e(TAG, "获取录像失败: " + msg + " | DeviceSerial: " + deviceSerial + " | CameraNo: " + mCameraNo + " | StartTime: " + startTime + " | EndTime: " + endTime); if (!isFinishing() && !isDestroyed()) { runOnUiThread(() -> Toast.makeText(FanHui.this, "获取录像失败: " + msg, Toast.LENGTH_SHORT).show()); } } } else { Log.e(TAG, "HTTP错误: " + responseCode); if (!isFinishing() && !isDestroyed()) { runOnUiThread(() -> Toast.makeText(FanHui.this, "网络请求失败: " + responseCode, Toast.LENGTH_SHORT).show()); } } conn.disconnect(); } catch (Exception e) { Log.e(TAG, "获取录像异常", e); if (!isFinishing() && !isDestroyed()) { runOnUiThread(() -> Toast.makeText(FanHui.this, "获取录像出错: " + e.getMessage(), Toast.LENGTH_SHORT).show()); } } }); } private List<VideoInfo> parseVideoData(JSONArray records) throws JSONException { List<VideoInfo> videos = new ArrayList<>(); SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss", Locale.getDefault()); for (int i = 0; i < records.length(); i++) { JSONObject record = records.getJSONObject(i); VideoInfo video = new VideoInfo(); video.id = record.optString("id"); // 转换为毫秒时间戳 video.startTime = record.optLong("startTime"); video.endTime = record.optLong("endTime"); // 格式化时间显示 Date startDate = new Date(video.startTime); Date endDate = new Date(video.endTime); video.timeRange = sdf.format(startDate) + " - " + sdf.format(endDate); videos.add(video); } return videos; } private void playVideo(VideoInfo video) { // 停止当前播放 stopPlayback(); // 获取播放地址 playWithTimeRange(video); } private void playWithTimeRange(VideoInfo video) { try { // 创建播放器实例 mEZPlayer = EZOpenSDK.getInstance().createPlayer(mDeviceSerial, mCameraNo); // 设置回调处理器 mEZPlayer.setHandler(mHandler); // 设置验证码 mEZPlayer.setPlayVerifyCode(mVerifyCode); // 关联播放视图 if (mSurfaceHolder != null) { mEZPlayer.setSurfaceHold(mSurfaceHolder); } // 开始播放云存储录像 - 直接使用时间范围 mEZPlayer.startPlayback(video.startTime, video.endTime); Toast.makeText(this, "开始播放录像: " + video.timeRange, Toast.LENGTH_SHORT).show(); } catch (Exception e) { Log.e(TAG, "播放录像失败", e); Toast.makeText(this, "播放录像失败: " + e.getMessage(), Toast.LENGTH_SHORT).show(); } } /** * 获取云存储录像回放地址并播放 */ // private void fetchPlaybackUrl(VideoInfo video) { // // 检查Activity状态 // if (isFinishing() || isDestroyed()) return; // // if (TextUtils.isEmpty(mDeviceSerial)) { // runOnUiThread(() -> Toast.makeText(FanHui.this, // "设备序列号为空", Toast.LENGTH_SHORT).show()); // return; // } // final String deviceSerial = mDeviceSerial.toUpperCase(); // // mExecutorService.execute(() -> { // try { // // 构建POST请求参数 // StringBuilder postData = new StringBuilder(); // postData.append("accessToken=").append(URLEncoder.encode(mAccessToken, "UTF-8")); // postData.append("&deviceSerial=").append(URLEncoder.encode(deviceSerial, "UTF-8")); // postData.append("&channelNo=").append(mCameraNo); // postData.append("&type=3"); // 3-云存储回放 // postData.append("&videoId=").append(URLEncoder.encode(video.id, "UTF-8")); // 传递录像ID // // URL url = new URL(PLAYBACK_URL_API); // HttpURLConnection conn = (HttpURLConnection) url.openConnection(); // conn.setRequestMethod("POST"); // conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); // conn.setDoOutput(true); // conn.setConnectTimeout(10000); // conn.setReadTimeout(10000); // // // 发送请求 // OutputStream os = conn.getOutputStream(); // os.write(postData.toString().getBytes("UTF-8")); // os.flush(); // os.close(); // // // 处理响应 // int responseCode = conn.getResponseCode(); // if (responseCode == HttpURLConnection.HTTP_OK) { // InputStream is = conn.getInputStream(); // BufferedReader reader = new BufferedReader(new InputStreamReader(is)); // StringBuilder response = new StringBuilder(); // String line; // while ((line = reader.readLine()) != null) { // response.append(line); // } // reader.close(); // // // 解析JSON响应 // JSONObject json = new JSONObject(response.toString()); // String code = json.optString("code", "0"); // if ("200".equals(code)) { // JSONObject data = json.getJSONObject("data"); // JSONArray urls = data.getJSONArray("urls"); // // if (urls.length() > 0) { // String playbackUrl = urls.getJSONObject(0).getString("url"); // runOnUiThread(() -> playWithUrl(playbackUrl, video)); // } else { // runOnUiThread(() -> // Toast.makeText(FanHui.this, "未获取到有效播放地址", Toast.LENGTH_SHORT).show()); // } // } else { // String msg = json.optString("msg", "未知错误"); // runOnUiThread(() -> // Toast.makeText(FanHui.this, "获取播放地址失败: " + msg, Toast.LENGTH_SHORT).show()); // } // } else { // runOnUiThread(() -> // Toast.makeText(FanHui.this, "网络请求失败: " + responseCode, Toast.LENGTH_SHORT).show()); // } // conn.disconnect(); // } catch (Exception e) { // runOnUiThread(() -> // Toast.makeText(FanHui.this, "获取播放地址出错: " + e.getMessage(), Toast.LENGTH_SHORT).show()); // } // }); // } /** * 使用URL播放云存储录像 - 修复后的方法 */ // private void playWithUrl(String playbackUrl, VideoInfo video) { // try { // // 停止当前播放 // stopPlayback(); // // // 创建播放器实例 // mEZPlayer = EZOpenSDK.getInstance().createPlayer(mDeviceSerial, mCameraNo); // // // 设置回调处理器 // mEZPlayer.setHandler(mHandler); // // // 设置验证码 // mEZPlayer.setPlayVerifyCode(mVerifyCode); // // // 关联播放视图 // if (mSurfaceHolder != null) { // mEZPlayer.setSurfaceHold(mSurfaceHolder); // } // // // 开始播放云存储录像 - 使用获取的播放地址 // mEZPlayer.startPlayback(video.startTime, video.endTime); // // Toast.makeText(this, "开始播放录像: " + video.timeRange, Toast.LENGTH_SHORT).show(); // } catch (Exception e) { // Log.e(TAG, "播放录像失败", e); // Toast.makeText(this, "播放录像失败: " + e.getMessage(), Toast.LENGTH_SHORT).show(); // } // } private void stopPlayback() { if (mEZPlayer != null) { try { mEZPlayer.stopPlayback(); mEZPlayer.release(); } catch (Exception e) { Log.e(TAG, "停止播放失败", e); } mEZPlayer = null; } } // 播放器回调处理器 - 增强回调处理 private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_PLAYBACK_PLAY_SUCCESS: Log.i(TAG, "回放播放成功"); Toast.makeText(FanHui.this, "回放播放成功", Toast.LENGTH_SHORT).show(); break; case MSG_PLAYBACK_PLAY_FAIL: Log.e(TAG, "回放播放失败"); BaseException error = (BaseException) msg.obj; int errorCode = error.getErrorCode(); String errorMsg = "播放失败: " + errorCode; if (errorCode == ErrorCode.ERROR_INNER_VERIFYCODE_NEED) { errorMsg = "需要验证码"; } else if (errorCode == ErrorCode.ERROR_INNER_VERIFYCODE_ERROR) { errorMsg = "验证码错误"; } else if (errorCode == ErrorCode.ERROR_TRANSF_ACCESSTOKEN_ERROR) { errorMsg = "accessToken无效"; } else if (errorCode == ErrorCode.ERROR_TRANSF_DEVICE_OFFLINE) { errorMsg = "设备离线"; } Toast.makeText(FanHui.this, errorMsg, Toast.LENGTH_LONG).show(); break; case MSG_PLAYBACK_STOP: Log.i(TAG, "回放停止"); break; } } }; @Override protected void onPause() { super.onPause(); // 优化线程池管理 if (mExecutorService != null) { mExecutorService.shutdownNow(); mExecutorService = null; } // 停止播放 stopPlayback(); } @Override protected void onResume() { super.onResume(); // 重新创建线程池 if (mExecutorService == null) { mExecutorService = Executors.newFixedThreadPool(2); } } @Override protected void onDestroy() { super.onDestroy(); stopPlayback(); // 使用shutdownNow立即终止线程 if (mExecutorService != null) { mExecutorService.shutdownNow(); } } // 录像信息数据结构 private static class VideoInfo { String id; long startTime; long endTime; String timeRange; } // 列表适配器 private class PlaybackAdapter extends BaseAdapter { @Override public int getCount() { return mVideoList.size(); } @Override public Object getItem(int position) { return mVideoList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = getLayoutInflater().inflate(R.layout.video_item_layout, parent, false); holder = new ViewHolder(); holder.timeTextView = convertView.findViewById(R.id.time_text); holder.durationTextView = convertView.findViewById(R.id.duration_text); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } VideoInfo video = mVideoList.get(position); // 计算持续时间(分钟) long durationMinutes = (video.endTime - video.startTime) / (1000 * 60); holder.timeTextView.setText(video.timeRange); holder.durationTextView.setText(durationMinutes + "分钟"); return convertView; } class ViewHolder { TextView timeTextView; TextView durationTextView; } } }解决上述代码中报错:'startPlayback(java.util.Calendar, java.util.Calendar)' in 'com.videogo.openapi.EZPlayer' cannot be applied to '(long, long)'
06-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值