我创建了一个自定义的 VideoView 类,它扩展了 VideoView 并声明了三个构造函数:
package com.tibo.webtv;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import org.apache.http.conn.ConnectTimeoutException;
import com.framework.utilityframe.log.log;
import com.framework.utilityframe.utility.utility;
import com.tibo.webtv.activities.PlayerActivity;
import com.tibo.webtv.database.objects.ServerResponseObject;
import com.tibo.webtv.database.objects.TVChannelObject;
import com.tibo.webtv.global.Global;
import com.tibo.webtv.util.TiboCacheKey;
import com.tibo.webtv.util.Util;
import com.tibo.webtv.web.TiboLog;
import com.tibo.webtv.web.WebHelper3;
import android.content.Context;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnInfoListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
import android.util.AttributeSet;
import android.widget.Toast;
import android.widget.VideoView;
public class CustomVideoView extends VideoView implements OnPreparedListener,OnErrorListener,OnInfoListener,OnCompletionListener
{
private long buffer_start_time;
private long buffer_end_time;
private String token;
private Handler handler = new Handler();
public CustomVideoView(Context context)
{
super(context);
}
public CustomVideoView(Context context, AttributeSet attrs)
{
super(context, attrs);
setOnPreparedListener(this);
setOnErrorListener(this);
setOnInfoListener(this);
}
public CustomVideoView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
public TVChannelObject getCurrentPlayingChannel()
{
return current_playing_channel;
}
public void setCurrentPlayingChannel(TVChannelObject current_playing_channel)
{
this.current_playing_channel = current_playing_channel;
}
public TVChannelObject getPreviousPlayingChannel()
{
return previous_playing_channel;
}
public void setPreviousPlayingChannel(TVChannelObject previous_playing_channel)
{
this.previous_playing_channel = previous_playing_channel;
}
private TVChannelObject current_playing_channel;
private TVChannelObject previous_playing_channel;
private String access_way;
private Date stream_started_time;
private long stream_play_time;
private boolean FLAG_STREAM_STARTED;
private String error_code;
@Override
public void stopPlayback()
{
new WatchingTime().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
super.stopPlayback();
}
@Override
public void onPrepared(MediaPlayer mp)
{
log.i("on Prepared");
FLAG_STREAM_STARTED = true;
stream_started_time = Calendar.getInstance().getTime();
mp.start();
}
@Override
public boolean onError(MediaPlayer mp, int what, int extra)
{
log.i("on onError");
error_code = what + "";
Toast.makeText(getContext(),getContext().getString(R.string.videoerror) + " " + error_code, Toast.LENGTH_LONG).show();
new BlackScreenError().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
if(what == MediaPlayer.MEDIA_ERROR_SERVER_DIED)
mp.reset();
else if(what == MediaPlayer.MEDIA_ERROR_UNKNOWN)
mp.reset();
return true;
};
@Override
public boolean onInfo(MediaPlayer mp, int what, int extra)
{
log.i("on onInfo");
if (what == MediaPlayer.MEDIA_INFO_BUFFERING_START)
{
buffer_start_time = System.currentTimeMillis();
}
else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END)
{
buffer_end_time = System.currentTimeMillis();
new Buffering_Problem().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
FLAG_STREAM_STARTED = false;
}
return true;
}
@Override
public void onCompletion(MediaPlayer mp)
{
super.setOnCompletionListener(this);
new WatchingTime().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
public void start(TVChannelObject channel)
{
super.start();
if(current_playing_channel == null)
{
current_playing_channel = channel;
}
else
{
previous_playing_channel = current_playing_channel;
current_playing_channel = channel;
Global.shared_preference.setValue(TiboCacheKey.PLAYING_CHANNEL, current_playing_channel.number);
Global.shared_preference.setValue(TiboCacheKey.LAST_CHANNEL_VIEWED, previous_playing_channel.number);
}
this.access_way = "-1";
}
public synchronized void start(TVChannelObject channel,String access_way)
{
super.start();
if(isPlaying())
{
this.stopPlayback();
}
if(current_playing_channel == null)
{
current_playing_channel = channel;
}
else
{
previous_playing_channel = current_playing_channel;
current_playing_channel = channel;
Global.shared_preference.setValue(TiboCacheKey.PLAYING_CHANNEL, current_playing_channel.number);
if(!utility.stringCompareIgnoreCase(current_playing_channel.videotype, "2"))
{
Global.shared_preference.setValue(TiboCacheKey.LAST_CHANNEL_VIEWED, current_playing_channel.number);
}
}
this.access_way = access_way;
if (current_playing_channel.token.trim().toLowerCase(Locale.getDefault()).toCharArray()[0] == '1')
{
log.i("Channel with token!!!");
new PlayStreamWithToken().execute("");
}
else
{
log.i("Channel without token!!!");
token = "";
setVideoURI(Uri.parse(current_playing_channel.url));
start();
closePlayerActivityAfterSpecificTime(1000 * 60 * 120);
}
new ChannelHits().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private void closePlayerActivityAfterSpecificTime(int milliseconds)
{
handler.removeCallbacks(ClosePlayerActivity);
handler.postDelayed(ClosePlayerActivity, milliseconds);
}
private Runnable ClosePlayerActivity = new Runnable() {
public void run() {
new ClosePlayerActivityAfterInActivity().execute(0);
}
};
private class PlayStreamWithToken extends AsyncTask<String, String, String>
{
ServerResponseObject<String> token_response = null;
@Override
protected void onPreExecute()
{
super.onPreExecute();
}
@Override
protected String doInBackground(String... params)
{
Thread.currentThread().setName("get token");
log.i("Channel with token!!!");
log.i("token url",current_playing_channel.TokenUrl);
try
{
token_response = WebHelper3.getToken(current_playing_channel.TokenUrl);
if(token_response != null)
{
if(token_response.status_code < 300)
{
if(!utility.stringCompareIgnoreCase(token_response.extra_data, ""))
{
token = token_response.extra_data;
log.i("token",token);
return Util.RESPONSE_OK;
}
}
else
{
//TODO handle error
return Util.RESPONSE_ERROR;
}
}
else
{
//TODO hanlde
token = "";
return Util.RESPONSE_NULL;
}
}
catch (ConnectTimeoutException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
return Util.RESPONSE_TIME_OUT;
}
return "";
}
protected void onPostExecute(String response)
{
if(utility.stringCompareIgnoreCase(response, Util.RESPONSE_OK))
{
String channel_url_to_play = current_playing_channel.url;
channel_url_to_play += token.trim();
log.i("url to play with token :", channel_url_to_play);
setVideoURI(Uri.parse(channel_url_to_play));
start();
closePlayerActivityAfterSpecificTime(1000 * 60 * 120);
}
else if(utility.stringCompareIgnoreCase(response, Util.RESPONSE_TIME_OUT))
{
//TODO handle timout and null
}
}
}
private class ClosePlayerActivityAfterInActivity extends AsyncTask<Integer, Integer, Integer>
{
@Override
protected Integer doInBackground(Integer... params)
{
Thread.currentThread().setName("SleepTask");
return 0;
}
@Override
protected void onPostExecute(Integer result)
{
if (isPlaying())
{
stopPlayback();
}
PlayerActivity.this_activity.finish();
}
}
private class Buffering_Problem extends AsyncTask<Integer, Integer, Integer>
{
@Override
protected void onPreExecute()
{
super.onPreExecute();
}
@Override
protected Integer doInBackground(Integer... params)
{
Thread.currentThread().setName("Buffering problem Custom VideoView");
if (FLAG_STREAM_STARTED)
{
TiboLog.logBufferingError(TiboLog.LIVE_TV_BUFFER_ERRORS,current_playing_channel.number, buffer_end_time - buffer_start_time, "Warning");
}
else
{
TiboLog.logBufferingError(TiboLog.LIVE_TV_BUFFER_ERRORS,current_playing_channel.number, buffer_end_time - buffer_start_time, "Error");
}
buffer_end_time = 0;
buffer_start_time = 0;
return 0;
}
}
private class BlackScreenError extends AsyncTask<Integer, Integer, Integer>
{
@Override
protected void onPreExecute()
{
super.onPreExecute();
}
@Override
protected Integer doInBackground(Integer... params)
{
Thread.currentThread().setName("Black Screen Error");
try
{
TiboLog.logSendError(TiboLog.LOGTYPE_ERROR,current_playing_channel.number, "Error Black Screen " + error_code);
}
catch (Exception e)
{
e.printStackTrace();
}
return 0;
}
}
private class ChannelHits extends AsyncTask<Integer, Integer, Integer>
{
@Override
protected void onPreExecute()
{
super.onPreExecute();
}
@Override
protected Integer doInBackground(Integer... params)
{
Thread.currentThread().setName("Channel Hit log");
TiboLog.logChannelHit(TiboLog.LIVE_TV_HITS,current_playing_channel.number, access_way, previous_playing_channel.number);
return 0;
}
}
private class WatchingTime extends AsyncTask<Integer, Integer, Integer>
{
@Override
protected void onPreExecute()
{
super.onPreExecute();
}
@Override
protected Integer doInBackground(Integer... params)
{
Thread.currentThread().setName("Watching Time Log");
if(stream_started_time != null)
{
long stream_ended_time = Calendar.getInstance().getTime().getTime();
stream_play_time = stream_ended_time - stream_started_time.getTime();
int playtime_in_seconds = (int) (stream_play_time)/1000;
log.i("playtime_in_seconds",playtime_in_seconds+"");
if (playtime_in_seconds >= 15 && playtime_in_seconds < 108000)
{
TiboLog.sendWatchingTime(TiboLog.LIVE_TV_WATCHING_TIME, current_playing_channel.number, playtime_in_seconds + "");
}
}
return 0;
}
}
}
xml文件如下:
<RelativeLayout
android:id="@+id/media_player_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<com.tibo.webtv.CustomVideoView --line 13
android:id="@+id/surface_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_gravity="center" />
</RelativeLayout>
当我在 android 4.1 上运行这个 apk 时, Activity 崩溃并且日志消息是:
11-13 11:36:27.962: E/AndroidRuntime(1040): FATAL EXCEPTION: main
11-13 11:36:27.962: E/AndroidRuntime(1040): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.tibo.webtv/com.tibo.webtv.activities.PlayerActivity}: android.view.InflateException: Binary XML file line #13: Error inflating class com.webtv.CustomVideoView
11-13 11:36:27.962: E/AndroidRuntime(1040): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2059)
11-13 11:36:27.962: E/AndroidRuntime(1040): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
11-13 11:36:27.962: E/AndroidRuntime(1040): at android.app.ActivityThread.access$600(ActivityThread.java:130)
11-13 11:36:27.962: E/AndroidRuntime(1040): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
11-13 11:36:27.962: E/AndroidRuntime(1040): at android.os.Handler.dispatchMessage(Handler.java:99)
11-13 11:36:27.962: E/AndroidRuntime(1040): at android.os.Looper.loop(Looper.java:137)
11-13 11:36:27.962: E/AndroidRuntime(1040): at android.app.ActivityThread.main(ActivityThread.java:4745)
11-13 11:36:27.962: E/AndroidRuntime(1040): at java.lang.reflect.Method.invokeNative(Native Method)
11-13 11:36:27.962: E/AndroidRuntime(1040): at java.lang.reflect.Method.invoke(Method.java:511)
11-13 11:36:27.962: E/AndroidRuntime(1040): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
11-13 11:36:27.962: E/AndroidRuntime(1040): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
11-13 11:36:27.962: E/AndroidRuntime(1040): at dalvik.system.NativeStart.main(Native Method)
11-13 11:36:27.962: E/AndroidRuntime(1040): Caused by: android.view.InflateException: Binary XML file line #13: Error inflating class com.webtv.CustomVideoView
11-13 11:36:27.962: E/AndroidRuntime(1040): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:698)
11-13 11:36:27.962: E/AndroidRuntime(1040): at android.view.LayoutInflater.rInflate(LayoutInflater.java:746)
11-13 11:36:27.962: E/AndroidRuntime(1040): at android.view.LayoutInflater.rInflate(LayoutInflater.java:749)
11-13 11:36:27.962: E/AndroidRuntime(1040): at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
11-13 11:36:27.962: E/AndroidRuntime(1040): at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
11-13 11:36:27.962: E/AndroidRuntime(1040): at android.view.LayoutInflater.inflate(LayoutInflater.java:352)
11-13 11:36:27.962: E/AndroidRuntime(1040): at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:256)
11-13 11:36:27.962: E/AndroidRuntime(1040): at android.app.Activity.setContentView(Activity.java:1867)
11-13 11:36:27.962: E/AndroidRuntime(1040): at com.tibo.webtv.activities.PlayerActivity.onCreate(PlayerActivity.java:188)
11-13 11:36:27.962: E/AndroidRuntime(1040): at android.app.Activity.performCreate(Activity.java:5008)
11-13 11:36:27.962: E/AndroidRuntime(1040): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1079)
11-13 11:36:27.962: E/AndroidRuntime(1040): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2023)
11-13 11:36:27.962: E/AndroidRuntime(1040): ... 11 more
11-13 11:36:27.962: E/AndroidRuntime(1040): Caused by: java.lang.ClassNotFoundException: com.webtv.CustomVideoView
11-13 11:36:27.962: E/AndroidRuntime(1040): at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:61)
11-13 11:36:27.962: E/AndroidRuntime(1040): at java.lang.ClassLoader.loadClass(ClassLoader.java:501)
11-13 11:36:27.962: E/AndroidRuntime(1040): at java.lang.ClassLoader.loadClass(ClassLoader.java:461)
11-13 11:36:27.962: E/AndroidRuntime(1040): at android.view.LayoutInflater.createView(LayoutInflater.java:552)
11-13 11:36:27.962: E/AndroidRuntime(1040): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:687)
11-13 11:36:27.962: E/AndroidRuntime(1040): ... 22 more
我想问题出在 OnInfoListener 上,因为当我删除它时一切正常。
最佳答案
问题出在包名上。交叉检查您在 xml 中使用的完全限定类名和您的类定义。
简而言之,当您按下 ctrl+鼠标左键时,您应该被重定向到类定义。
它应该是 com.packagename.yourCustomclassName
关于android - 二进制 XML 文件行 #13 : Error inflating class on a class that extends VideoView,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33691301/
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时
我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,
Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题
对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl
我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚
使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta
我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为
好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信
我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A